如何对值类型使用协方差?

How to use covariance for value types?

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

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 解释原因:

Basically, variance applies when the CLR can ensure that it doesn't need to make any representational change to the values. References all look the same - so you can use an IEnumerable<string> as an IEnumerable<object> without any change in representation; the native code itself doesn't need to know what you're doing with the values at all, so long as the infrastructure has guaranteed that it will definitely be valid.

For value types, that doesn't work - to treat an IEnumerable<int> as an IEnumerable<object>, the code using the sequence would have to know whether to perform a boxing conversion or not.

嗯,大便。至少 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 );
}