我有以下情况:
//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
}
}
我试图用Activator(Reflection)来解决它,但是我遇到了一个问题:没有真正拥有类型为ISaberpsicologiaExceptionHandler <[runtime exceptiontype]>的对象,所以我不能正确使用该类型。
总而言之,问题是我有一个异常类型,并且我想获取该异常类型的ISaberpsicologiaExceptionHandler,我想我可以使用更多的反射来执行'Result'方法,但是我想这样做一点点优雅。
答案 0 :(得分:2)
您没有显示实现ISaberpsicologiaExceptionHandler<T>
的类的完整上下文。但是仅从该接口的定义来看,我并不一定要是通用接口。
一些可能的解决方案:
使方法通用:
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));
}
}
用法:
要访问ISaberpsicologiaExceptionHandler.Result
,只需将其强制转换为非通用基本接口ISaberpsicologiaExceptionHandler
,无论实现类型是什么
catch (MovedPermanentlyException exception)
{
var handler = Activator.CreateInstance(handlerType) as ISaberpsicologiaExceptionHandler;
handler.Result(exception);
}
使用专用界面:
// 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
}
用法:
要访问ISaberpsicologiaExceptionHandler.Result
,只需将其强制转换为非通用的,不太专业的基本接口ISaberpsicologiaExceptionHandler
,无论实现类型是什么。
catch (MovedPermanentlyException exception)
{
var handler = Activator.CreateInstance(handlerType) as ISaberpsicologiaExceptionHandler;
handler.Result(exception);
}
使用反射:
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));
}
}
用法:
要访问ISaberpsicologiaExceptionHandler.Result
,只需将其强制转换为非通用基本接口ISaberpsicologiaExceptionHandler
,无论实现类型是什么
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);
}
推荐的解决方案。
在调用时使用适当的具体实现:
我不知道您的异常处理程序的概念。但是由于您始终知道要捕获的特定异常,因此可以创建适当的实例(此时也可以使用工厂):
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);
}
还有更多解决方案,所以我休息一下。它只是归结为避免使用未知的泛型类型的代码引用此未知类型的成员。我认为这始终是可能的,只是一个好的设计问题。
答案 1 :(得分:1)
我使用了 System.Linq.Expressions
的通用方法考虑到异常类型,将构建一个委托来调用所需的函数
LambdaExpression buildHandlerDelegate(Type exceptionType, Type handlerType) {
var type = typeof(ISaberpsicologiaExceptionHandler<>);
var genericType = type.MakeGenericType(exceptionType); //ISaberpsicologiaExceptionHandler<MyException>
var handle = genericType.GetMethod("Result", new[] { exceptionType });
var func = typeof(Func<,>);
var delegateType = func.MakeGenericType(typeof(Exception), typeof(ActionResult));
//Intension is to create the following expression:
// Func<Exception, ActionResult> function =
// (exception) => (new handler()).Result((MyException)exception);
// exception =>
var exception = Expression.Parameter(typeof(Exception), "exception");
// new handler()
var newHandler = Expression.New(handlerType);
// (MyException)exception
var cast = Expression.Convert(exception, exceptionType);
// (new handler()).Result((MyException)exception)
var body = Expression.Call(newHandler, handle, cast);
//Func<TException, ActionResult> (exception) =>
// (new handler()).Result((MyException)exception)
var expression = Expression.Lambda(delegateType, body, exception);
return expression;
}
,并且可以与以下过滤器一起使用
//...
var exceptionType = exception.GetType();
var handlerType = mapping[exceptionType];
var handler = buildHandlerDelegate(exceptionType, handlerType).Compile();
var result = handler.DynamicInvoke(exception);
context.Result = (IActionResult)result;
//...
这是完整的实现
public class ExceptionHandlerFilter : ExceptionFilterAttribute {
public override void OnException(ExceptionContext context) {
base.OnException(context);
HandleResponseCodeByExceptionType(context);
}
static readonly Dictionary<Type, Type> mapping = new Dictionary<Type, Type>
{
{ typeof(MovedPermanentlyException), typeof(MovedPermanentlyExceptionHandler) }
};
private void HandleResponseCodeByExceptionType(ExceptionContext context) {
var exception = context.Exception;
if (!CanHandle(exception)) {
return;
}
var exceptionType = exception.GetType();
var handlerType = mapping[exceptionType];
var handler = buildHandlerDelegate(exceptionType, handlerType).Compile();
var result = handler.DynamicInvoke(exception);
context.Result = (IActionResult)result;
}
LambdaExpression buildHandlerDelegate(Type exceptionType, Type handlerType) {
var type = typeof(ISaberpsicologiaExceptionHandler<>);
var genericType = type.MakeGenericType(exceptionType); //ISaberpsicologiaExceptionHandler<MyException>
var handle = genericType.GetMethod("Result", new[] { exceptionType });
var func = typeof(Func<,>);
var delegateType = func.MakeGenericType(typeof(Exception), typeof(ActionResult));
//Intension is to create the following expression:
// Func<Exception, ActionResult> function =
// (exception) => (new handler()).Result((MyException)exception);
// exception =>
var exception = Expression.Parameter(typeof(Exception), "exception");
// new handler()
var newHandler = Expression.New(handlerType);
// (MyException)exception
var cast = Expression.Convert(exception, exceptionType);
// (new handler()).Result((MyException)exception)
var body = Expression.Call(newHandler, handle, cast);
//Func<TException, ActionResult> (exception) =>
// (new handler()).Result((MyException)exception)
var expression = Expression.Lambda(delegateType, body, exception);
return expression;
}
}
使用以下单元测试来验证预期行为
[TestClass]
public class ExceptionHandlerFilterTests {
[TestMethod]
public void Should_Handle_Custom_Exception() {
//Arrange
var subject = new ExceptionHandlerFilter();
var url = "http://example.com";
var context = new ExceptionContext(Mock.Of<ActionContext>(), new List<IFilterMetadata>()) {
Exception = new MovedPermanentlyException(url)
};
//Act
subject.OnException(context);
//Assert
context.Result.Should()
.NotBeNull()
.And.BeOfType<RedirectResult>();
}
}
答案 2 :(得分:0)
使用if...else
或switch
语句可能会更好。您的代码可能看起来像这样
private void HandleResponseCodeByExceptionType(ExceptionContext context)
{
var exception = context.Exception;
if (!CanHandle(exception)) return;
var exceptionType = exception.GetType();
if (exceptionType == typeof(MovedPermanantelyException)) {
var handler = new MovePermanentlyExceptionHandler();
handler.Result(exception);
}
else {
// chain the rest of your handlers in else if statements with a default else
}
}
这具有明显的优势,即允许您为这些处理程序显式使用构造函数,而不是尝试通过反射创建它们。借助反射,如果不进行大量额外的工作和代码修改,您将无法向构造函数添加其他参数。