您无法对值类型使用协方差:
Func<string> refTypeFunc = () => "foo";
Func<int> valueTypeFunc = () => 123;
Func<object> a = refTypeFunc; // works
Func<object> b = valueTypeFunc; // doesn't work
This answer by Jon Skeet解释了原因:
好吧,大便。至少基本上,当CLR可以确保它不需要对值进行任何代表性更改时,方差适用。引用看起来都一样 - 所以你可以使用
IEnumerable<string>
作为IEnumerable<object>
,而不会改变表示形式;本机代码本身并不需要知道您对这些值所做的事情,只要基础设施确保它肯定有效。对于不起作用的值类型 - 将
IEnumerable<int>
视为IEnumerable<object>
,使用序列的代码必须知道是否执行装箱转换。< / p>
Func
你可以这样做:
Func<object> c = () => valueTypeFunc();
然而,在大多数情况下,这种简单的出路无法使用。假设我将接口定义为:
interface ICovariant<out T>
{
Func<T> InnerFunc { get; }
}
现在,如果我有ICovariant<T>
,我就无法将其投放到ICovariant<object>
,我认为没有简单的方法。我知道T
可以是object
- 一切都可以。在这种情况下我该怎么办?如果没有简单的解决方法,还有什么是最好的方法?
答案 0 :(得分:2)
您必须对协变界面进行特殊实现才能为您进行转换。像这样:
public class Boxer<T, U> : ICovariant<T> where U : struct, T
{
public Boxer( ICovariant<U> foo )
{
mFoo = foo;
}
public Func<T> CallMe => () => mFoo.CallMe();
private readonly ICovariant<U> mFoo;
}
现在允许您包装ICovariant<T>
接口的值类型实现。如果您发现要输入的所有通用参数都令人讨厌,您可以创建一个静态方法来为您进行演绎:
static void BoxIt<T, U>( IFoo<U> fooU, out IFoo<T> fooT ) where U : struct, T
{
fooT = new Boxer<T, U>( fooU );
}