如何使用协方差值类型?

时间:2016-04-01 19:08:57

标签: c# covariance

您无法对值类型使用协方差:

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 - 一切都可以。在这种情况下我该怎么办?如果没有简单的解决方法,还有什么是最好的方法?

1 个答案:

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