无法将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
不以任何方式从List
inheritance。 这就像比较string
到int
。
这并不是说类型之间根本没有关系; 只是说有关的关系不是inheritance。 相反,我们在这两种类型之间存在专门化关系。 .Net 4.0及更高版本中的某些类型支持称为协方差的特化属性。 共同和反方差允许generics类型的特定部分在某些情况下变化 (注意那里的根词)。 List
不是这些类型之一。
但是在这种情况下我会做什么(我不确定你有.Net 4.0)会使整个方法变得通用:
public void MyMethod(IEnumerable items) where T : IFoo { //... }
请注意,我还使用了IEnumerable
而不是List
。 您仍然可以将列表传递给此函数,因为IEnumerable
和List
之间存在inheritance/实现关系。 您还可以处理数组和任何其他支持的序列。 IEnumerable也是支持协方差的类型之一。 几乎任何需要List
参数的方法都应该更新为使用IEnumerable
。
您需要创建一个新的List
并将第一个列表中的所有项添加到其中。
好吧,你可以将它转换为声明为List
新列表,这就是你需要的:
List list = new List (); theMethodThatTakesIFooList(list.ToList());
这将工作我猜…因为Foo可以转换为IFoo,而ToList返回新类型specefied。