如何将 Func<T, object> 转换为 Func<Exception, object>
How to cast Func<T, object> to 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<,>
?
我认为这样的转换是不可能的,因为就编译器而言,这两个函数声明完全无关。
您可以尝试将 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);
}
然后你的字典可以包含各种异常类型的具体实现。
字典 _exceptionFormatters
字典正在存储“采用任何 Exception
s 的函数”,但您为字典提供了一个“采用特定 Exception
即 T
的函数”。显然,“采用特定 Exception
即 T
的函数”不是一种“采用任何 Exception
的函数”。
要了解为什么这会中断,假设:
AddExceptionFormatter<NotFoundException>(x => x.Foo);
其中 Foo
是 NotFoundException
拥有的 属性,类型为 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");
}
}
我想创建一个字典来存储异常和该异常的格式化程序。
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<,>
?
我认为这样的转换是不可能的,因为就编译器而言,这两个函数声明完全无关。
您可以尝试将 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);
}
然后你的字典可以包含各种异常类型的具体实现。
字典
字典正在存储“采用任何 Exception
s 的函数”,但您为字典提供了一个“采用特定 Exception
即 T
的函数”。显然,“采用特定 Exception
即 T
的函数”不是一种“采用任何 Exception
的函数”。
要了解为什么这会中断,假设:
AddExceptionFormatter<NotFoundException>(x => x.Foo);
其中 Foo
是 NotFoundException
拥有的 属性,类型为 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");
}
}