Func <>的List <>,使用泛型返回类型编译错误,但为什么呢?

Ric*_*ett 16 c# generics .net-4.0 func

这是一个冗长的问题,所以请耐心等待.

我需要在一组字符串和每个字符串的相应泛型方法调用之间创建一个映射.但是我遇到了一个编译问题,向下解释.

在我的场景中,我正在使用a Dictionary<>,但问题同样存在于a List<>.为简单起见,我List<>在下面的示例中使用了a .

考虑这三个类:

public abstract class MyBase { /* body omitted */  }
public class MyDerived1 : MyBase { /* body omitted */  }
public class MyDerived2 : MyBase { /* body omitted */  }
Run Code Online (Sandbox Code Playgroud)

还有一些其他类的方法:

public class Test
{
    public T GetT<T>() where T : MyBase { /* body omitted */ }
}
Run Code Online (Sandbox Code Playgroud)

在另一个课程中,我可以声明List<Func<MyBase>>如下:

public class SomeClass
{
    public void SomeFunc()
    {
        var test = new Test();

        var list1 = new List<Func<MyBase>>
            {
                test.GetT<MyDerived1>,
                test.GetT<MyDerived2>
            };
    }
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好.

但是,如果我想要一个返回如下通用类的函数,该怎么办:

public class RetVal<T> where T : MyBase { /* body omitted */ }

public class Test
{
    public RetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想List<>用这个函数创建一个等价物.即列表>>?

public class Class1
{
    public void SomeFunc()
    {
        var test = new Test();

        var list2 = new List<Func<RetVal<MyBase>>>
            {
                test.GetRetValT<MyDerived1>, // compile error
                test.GetRetValT<MyDerived2> // compile error
            };
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到编译错误Expected a method with 'RetVal<MyBase> GetRetValT()' signature.

那么,有什么方法可以解决这个问题,还是有一种替代方法可以用来创建我的字符串......泛型方法调用映射?

Str*_*ior 16

C#仅允许接口上的协方差.这意味着您无法自动投射RetVal<MyDerived1>RetVal<MyBase>.如果RetVal应该是协变的,为它创建一个接口,如下所示:

public interface IRetVal<out T>
{

}
public class RetVal<T> : IRetVal<T> where T : MyBase { /* body omitted */ }

public class Test
{
    public IRetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

那么这段代码将起作用:

    var list2 = new List<Func<IRetVal<MyBase>>>
        {
            test.GetRetValT<MyDerived1>,
            test.GetRetValT<MyDerived2>
        };
Run Code Online (Sandbox Code Playgroud)


Chr*_*ain 7

问题是泛型的经典协方差/逆变.你假设因为MyDerived1MyDerived2继承MyBase,一个RetVal<MyDerived1>继承自RetVal<MyBase>,而不是继承.

解决此问题的最简单方法可能是将代码更改为:

var list2 = new List<Func<RetVal<MyBase>>>
        {
            () => (MyBase)test.GetRetValT<MyDerived1>,
            () => (MyBase)test.GetRetValT<MyDerived2>
        };
Run Code Online (Sandbox Code Playgroud)

或者更好,正如JS在评论中指出的那样,RetVal<T>如果可能的话,只需改为协变:

public interface IRetVal<out T> { ... }

public class RetVal<T> : IRetVal<T> { ... }
Run Code Online (Sandbox Code Playgroud)

  • 不完全 - 在C#4中,`Func <T>`*在T中是*协变,这就是第一个例子有效的原因.这是"RetVal <T>",这里没有协变性. (3认同)