如何对值类型使用协方差

  • 本文关键字:方差 类型 c# covariance
  • 更新时间 :
  • 英文 :


不能对值类型使用协方差:

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 );
}

相关内容

  • 没有找到相关文章

最新更新