如何将 Func<T, object> 转换为 Func<Exception, object>



我想创建一个字典来存储异常和该异常的格式化程序。

Dictionary<Type, Func<Exception, ApiErrorResponse>>

我可以像这个一样直接将它们添加到字典中

Dictionary<Type, Func<Exception, ApiErrorResponse>> _exceptionFormatters =
new Dictionary<Type, Func<Exception, ApiErrorResponse>>
{
{
typeof(ValidationException),
ex =>
{
var e = ex as ValidationException;
return new ApiErrorResponse(HttpStatusCode.UnprocessableEntity, e.Errors);
}
},
{
typeof(NotFoundException),
ex =>
{
var e = ex as NotFoundException;
return new ApiErrorResponse(HttpStatusCode.NotFound, e.Message);
}
}
};

但我想用通用的AddExceptionFormatter方法

public static void AddExceptionFormatter<T>(Func<T, ApiErrorResponse> Value) where T : Exception
{
if (_exceptionFormatters.ContainsKey(typeof(T)))
_exceptionFormatters[typeof(T)] = (Func<Exception, ApiErrorResponse>)Value;
else
_exceptionFormatters.Add(typeof(T), (Func<Exception, ApiErrorResponse>)Value);
}

以下是我调用它的方法。

AddExceptionFormatter<NotFoundException>(ex =>
{
return new ApiErrorResponse(
HttpStatusCode.NotFoundException,
nameof(NotFoundException),
ex.Message);
});

但不幸的是,我Unable to cast object of type 'System.Func`2[NotFoundException,ApiErrorResponse]' to type 'System.Func`2[System.Exception,ApiErrorResponse]'.

有什么想法吗?我该怎么投Func<,>

字典正在存储"取任何CCD_ 4s"的函数;,但你给字典打了个"问号";取特定CCD_ 5即CCD_;。显然;采用作为CCD_ 8"的特定CCD_;不是一种";取任何Exceptions的函数";。

要了解为什么会出现这种情况,请假设:

AddExceptionFormatter<NotFoundException>(x => x.Foo);

其中FooNotFoundException具有的属性,并且是类型ApiErrorResponse

现在,如果我做这个顽皮的事情:

_exceptionFormatters[typeof(NotFoundException)](new Exception())

很明显,这通过了类型检查,因为我可以将Exception传递给Func<Exception, ApiErrorResponse>,但Exception实际上没有Foo属性!这就是为什么不允许你选角的原因。

这里的根本问题是,你不能向编译器保证";如果typeof(X)是字典的关键字之一,则_exceptionFormatters[typeof(X)]将是类型Func<X, ApiErrorResponse>";。C#的类型系统不够强大,无法支持这一点。

当有人做出上述顽皮行为时,你需要决定你想发生什么。假设您想要抛出一个异常,您可以使用lambda:

public static void AddExceptionFormatter<T>(Func<T, ApiErrorResponse> Value) where T : Exception
{
_exceptionFormatters[typeof(T)] = x => Value((T)x);
}

这将导致抛出InvalidCastException。你当然可以在lambda中做一些更花哨的事情:

x => {
if (x is T t) {
return Value(t);
} else {
throw new YourCustomException("some custom message");
}
}

我认为这样的强制转换是不可能的,因为就编译器而言,这两个函数声明完全不相关。

您可以尝试将AddExceptionFormatter的声明更改为

public static void AddExceptionFormatter<T>(Func<Exception, ApiErrorResponse> Value) where T : Exception

那么就没有必要铸造任何东西了。

您还可以使用接口来调用格式化函数和通用实现。尽管这更为复杂,也可能不那么具有表演性。我没有尝试编译下面的代码,所以把它当作一个指南。

interface IExceptionFormatter
{
ApiErrorResponse Format(Exception)
} 
class ExceptionFormatter<T> : IExceptionFormatter
{
private Func<T, ApiErrorResponse> _formatDelegate;
public ExceptionFormatter(Func<T, ApiErrorResponse> formatDelegate)
{
_formatDelegate = formatDelegate;
}
public ApiErrorResponse Format(Exception ex)
{
return _formatDelegate(ex);
}
}
public static void AddExceptionFormatter<T>(Func<T, ApiErrorResponse> Value) where T : Exception
{
Type templateType= typeof(ExceptionFormatter<>);
Type[] args = {typeof(T)};
Type desiredType = templateType.MakeGenericType(args);
if (_exceptionFormatters.ContainsKey(typeof(T)))
_exceptionFormatters[typeof(T)] = Activator.CreateInstance(desiredType, Value);
else
_exceptionFormatters.Add(typeof(T), Activator.CreateInstance(desiredType, Value);
}

然后,您的字典可以包含各种异常类型的特定实现。

词典<类型,IExceptionFormatter&gt_例外格式化程序

相关内容

最新更新