C#编译器无法识别正在实现接口的类

Fre*_*eek 12 .net c# generics .net-4.0 covariance

以下代码无法编译(使用VS2010),我不明白为什么.编译器应该能够推断出List<TestClass>"兼容"(抱歉缺少一个更好的词)IEnumerable<ITest>,但不知何故它没有.我在这里错过了什么?


interface ITest {
    void Test();
}


class TestClass : ITest {
    public void Test() {
    }
}

class Program {
    static void Test(IEnumerable<ITest> tests) {
        foreach(var t in tests) {
            Console.WriteLine(t);
        }
    }
    static void Main(string[] args) {
        var lst = new List<TestClass>();

        Test(lst); // fails, why?

        Test(lst.Select(t=>t as ITest)); //success

        Test(lst.ToArray()); // success
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器给出了两个错误:

  1. 'ConsoleApplication1.Program.Test(System.Collections.Generic.IEnumerable <ConsoleApplication2.ITest>)'的最佳重载方法匹配有一些无效的参数

  2. 参数1:无法从'System.Collections.Generic.List <ConsoleApplication2.TestClass>'转换为'System.Collections.Generic.IEnumerable <ConsoleApplication2.ITest>'

Sim*_*ens 8

你要做的是所谓的协方差 - 从较窄的类型(TestClass)转换为更宽的类型(ITest).这是你将一直习惯的东西,例如当你从一个浮点数转换为一个双精度时.

不幸的是.Net 3.5及更低版本不支持泛型类中的协方差.

.Net 4.0现在确实支持泛型中的协方差(和逆变),前提是这些泛型类是使用out协变类型和in反变量类型的关键字编译的.IEnumerable在.Net 4.0中定义为协变.如果右键单击该IEnumerable类型并单击"转到定义",您将看到:

public interface IEnumerable<out T> : IEnumerable
Run Code Online (Sandbox Code Playgroud)

如果您使用的是VS2010,则需要确保您的项目的目标是.net 4.0.这可以从项目属性更改.右键单击项目,选择属性,转到"应用程序"选项卡,然后检查"目标框架"是否为.Net 4.

MSDN有更多信息.