I have the following scenario:
//Base exception type
public class SaberpsicologiaException : Exception
{
}
//One of the derived exception class
public class MovedPermanentlyException : SaberpsicologiaException
{
public string CannonicalUri { get; private set; }
public MovedPermanentlyException(string cannonicalUri)
: base($"Moved permanently to {cannonicalUri}")
{
this.CannonicalUri = cannonicalUri;
}
}
interface ISaberpsicologiaExceptionHandler<T>
where T : SaberpsicologiaException
{
ActionResult Result(T exception);
}
public class MovedPermanentlyExceptionHandler
: ISaberpsicologiaExceptionHandler<MovedPermanentlyException>
{
public ActionResult Result(MovedPermanentlyException exception)
{
var redirectResult = new RedirectResult(exception.CannonicalUri);
redirectResult.Permanent = true;
return redirectResult;
}
}
public class ExceptionHandlerFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
HandleResponseCodeByExceptionType(context);
}
private void HandleResponseCodeByExceptionType(ExceptionContext context)
{
var exception = context.Exception;
if (!CanHandle(exception))
{
return;
}
var mapping = new Dictionary<Type, Type>
{
{ typeof(MovedPermanentlyException), typeof(MovedPermanentlyExceptionHandler) }
};
var handlerType = mapping[exception.GetType()];
var handler = Activator.CreateInstance(handlerType);
handler.Result(exception); //<- compilation error
//handler is type "object" and not MovedPermanentlyExceptionHandler
}
}
I tried to resolve it with the Activator (Reflection), but I get to the problem of not really having and object of type ISaberpsicologiaExceptionHandler< [runtime exceptiontype] > so I can't have use the type properly.
In summary the problem is that I have an exception type and I want to get the ISaberpsicologiaExceptionHandler for that exception type, I guess I could use more reflection to just execute the 'Result' method, but I would like to do it a little bit more ellegant.
You didn't show the full context of your class that implements ISaberpsicologiaExceptionHandler<T>
. But just from the definition of this interface I would say it doesn't need to be a generic interface.
A few possible solutions:
Make the method generic:
interface ISaberpsicologiaExceptionHandler
{
ActionResult Result<TException>(TException exception) where TException : SaberpsicologiaException;
}
public class MovedPermanentlyExceptionHandler : ISaberpsicologiaExceptionHandler
{
public ActionResult Result<TException>(TException exception) where TException : SaberpsicologiaException
{
if (exception is MovedPermanentlyException movedPermanentlyException)
{
var redirectResult = new RedirectResult(movedPermanentlyException.CannonicalUri);
redirectResult.Permanent = true;
return redirectResult;
}
throw new InvalidArgumentException("Exception type not supported", nameof(exception));
}
}
Usage:
To access ISaberpsicologiaExceptionHandler.Result
just cast to the non-generic base interface ISaberpsicologiaExceptionHandler
no matter the implementing type
catch (MovedPermanentlyException exception)
{
var handler = Activator.CreateInstance(handlerType) as ISaberpsicologiaExceptionHandler;
handler.Result(exception);
}
Use specialized interfaces:
// General interface
interface ISaberpsicologiaExceptionHandler
{
ActionResult Result(Exception exception);
}
// Specialized interface
interface IMovedPermanentlyExceptionHandler : ISaberpsicologiaExceptionHandler
{
ActionResult Result(MovedPermanentlyException exception);
}
public class MovedPermanentlyExceptionHandler : IMovedPermanentlyExceptionHandler
{
public ActionResult Result(MovedPermanentlyException exception)
{
var redirectResult = new RedirectResult(exception.CannonicalUri);
redirectResult.Permanent = true;
return redirectResult;
}
#region Implementation of ISaberpsicologiaExceptionHandler
// Explicit interface implementation
ActionResult ISaberpsicologiaExceptionHandler.Result(Exception exception)
{
if (exception is MovedPermanentlyException movedPermanentlyException)
{
return Result(movedPermanentlyException);
}
throw new InvalidArgumentException("Exception type not supported", nameof(exception));
}
#endregion
}
Usage:
To access ISaberpsicologiaExceptionHandler.Result
just cast to the non-generic less specialized base interface ISaberpsicologiaExceptionHandler
no matter the implementing type.
catch (MovedPermanentlyException exception)
{
var handler = Activator.CreateInstance(handlerType) as ISaberpsicologiaExceptionHandler;
handler.Result(exception);
}
Use reflection:
interface ISaberpsicologiaExceptionHandler
{
ActionResult Result<TException>(TException exception) where TException : SaberpsicologiaException;
}
public class MovedPermanentlyExceptionHandler : ISaberpsicologiaExceptionHandler
{
public ActionResult Result<TException>(TException exception) where TException : SaberpsicologiaException
{
if (exception is MovedPermanentlyException movedPermanentlyException)
{
var redirectResult = new RedirectResult(movedPermanentlyException.CannonicalUri);
redirectResult.Permanent = true;
return redirectResult;
}
throw new InvalidArgumentException("Exception type not supported", nameof(exception));
}
}
Usage:
To access ISaberpsicologiaExceptionHandler.Result
just cast to the non-generic base interface ISaberpsicologiaExceptionHandler
no matter the implementing type
catch (MovedPermanentlyException exception)
{
var handler = Activator.CreateInstance(handlerType) as ISaberpsicologiaExceptionHandler;
MethodInfo reflectedMethod = handlerType.GetMethod("Result");
MethodInfo genericMethod = reflectedMethod.MakeGenericMethod(exception.GetType());
object[] args = {exception};
genericMethod.Invoke(this, args);
}
Recommended solution.
Use the proper concrete implementation on invocation:
I don't know the concept of your exception handler. But since you always know which specific exception you want to catch you can create the proper instance (using a factory at this point is also an option):
try
{
// Do something that can throw a MovedPermanentlyException
}
catch (MovedPermanentlyException e)
{
var movedPermanentlyExceptionHandler = new MovedPermanentlyExceptionHandler();
movedPermanentlyExceptionHandler.Result(e);
}
catch (SomeOtherException e)
{
var someOtherExceptionHandler = new SomeOtherExceptionHandler();
someOtherExceptionHandler.Result(e);
}
There are more solutions so I just take a break. It just boils down to avoid code that uses unknown generic types where members of this unknown type are referenced. I argue that this is always possible and just a question of good design.