不能对值类型使用协方差:
Func<string> refTypeFunc = () => "foo";
Func<int> valueTypeFunc = () => 123;
Func<object> a = refTypeFunc; // works
Func<object> b = valueTypeFunc; // doesn't work
Jon Skeet的回答解释了原因:
基本上,当CLR可以确保它不需要对值进行任何表示性更改时,方差就适用了。引用看起来都是一样的,所以您可以将
IEnumerable<string>
用作IEnumerable<object>
,而不会对表示形式进行任何更改;本机代码本身根本不需要知道你对这些值做了什么,只要基础结构保证了它肯定是有效的。对于值类型,这不起作用——要将
IEnumerable<int>
视为IEnumerable<object>
,使用序列的代码必须知道是否执行装箱转换。
嗯,大便。至少对于Func
,你可以这样做:
Func<object> c = () => valueTypeFunc();
然而,这种简单的解决方法在大多数情况下都无法使用。假设我有一个接口定义为:
interface ICovariant<out T>
{
Func<T> InnerFunc { get; }
}
现在,如果我有一个ICovariant<T>
,我不能把它转换成ICovariant<object>
,我看不出简单的方法。我知道T
可以是object
——一切都可以。这种情况下我该怎么办?如果没有简单的解决方法,还有什么是最好的方法?
您必须对协变接口进行特殊实现才能进行转换。类似这样的东西:
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 );
}