无法将List 传递给期望List 的方法,其中Foo:IFoo

我有一个实现IFoo接口的类Foo 。 我有一个方法将List作为参数。 但是,它无法从List转换为List – 这让我感到惊讶,因为Foo实现了IFoo接口。

我怎样才能解决这个问题,为什么会出现这种情况? (总是很好地从错误中吸取教训)

这是因为List不是协变的。 有关详细信息,请参阅C#中的协方差和逆变 。

如果您可以使您的方法在IEnumerable工作,那么这将起作用(您可以将List传递到.NET 4中的IEnumerable ,因为它定义了IEnumerable )。

List不是协变的原因,顺便说一下,是因为它没有定义只读合同。 由于List明确允许您添加元素(通过Add(T) ),因此允许协方差工作是不安全的。 如果这是允许的,那么该方法可以期望能够将类型Bar的元素(如果Bar派生自IFoo )添加到列表中,但这会失败,因为列表实际上List ,而不是List 。 由于IEnumerable只允许您遍历列表,但不修改它,因此它可以是协变的,并且在这种情况下只是按预期工作。

信不信由你,这不是类型安全的。

请考虑以下代码:

 List fooList = new List(); List iFooList = fooList; //Illegal iFooList.Add(new SomeOtherFoo()); //That's not Foo! 

你要求协变转换; 这只适用于不可变类型。
此外,.Net仅支持接口的协方差。

更改方法以获取IEnumerable ,它将起作用。

请记住,generics类型的特化之间没有任何inheritance或实现关系。 List不以任何方式从Listinheritance。 这就像比较stringint

这并不是说类型之间根本没有关系; 只是说有关的关系不是inheritance。 相反,我们在这两种类型之间存在专门化关系。 .Net 4.0及更高版本中的某些类型支持称为协方差的特化属性。 共同和反方差允许generics类型的特定部分在某些情况下变化 (注意那里的根词)。 List不是这些类型之一。

但是在这种情况下我会做什么(我不确定你有.Net 4.0)会使整个方法变得通用:

 public void MyMethod(IEnumerable items) where T : IFoo { //... } 

请注意,我还使用了IEnumerable而不是List 。 您仍然可以将列表传递给此函数,因为IEnumerableList之间存在inheritance/实现关系。 您还可以处理数组和任何其他支持的序列。 IEnumerable也是支持协方差的类型之一。 几乎任何需要List参数的方法都应该更新为使用IEnumerable

您需要创建一个新的List并将第一个列表中的所有项添加到其中。

好吧,你可以将它转换为声明为List新列表,这就是你需要的:

 List list = new List(); theMethodThatTakesIFooList(list.ToList()); 

这将工作我猜…因为Foo可以转换为IFoo,而ToList返回新类型specefied。