我有一个实现Foo
接口的类IFoo
。我有一个方法,以List<IFoo>
为参数。但是,它无法从List<Foo>
转换为List<IFoo>
- 这让我感到惊讶,因为Foo
实现了IFoo
接口。
我如何解决这个问题,为什么会发生这种情况? (总是很好地从错误中吸取教训)
答案 0 :(得分:12)
这是因为List<T>
不是协变的。有关详细信息,请参阅Covariance and Contravariance in C#。
如果您可以让您的方法在IEnumerable<T>
上运行,那么这将有效(您可以将List<Foo>
传递到.NET 4中的IEnumerable<IFoo>
,因为它已定义为IEnumerable<out T>
)
List<T>
不合作的原因,顺便说一下,是因为它没有定义只读合同。由于List<T>
明确允许您添加元素(通过Add(T)
),因此允许协方差工作并不安全。如果允许这样做,该方法将期望能够将类型Bar
的元素(如果Bar
派生自IFoo
)添加到列表中,但这会失败,因为列表是真的是List<Foo>
,而不是List<IFoo>
。由于IEnumerable<T>
只允许您遍历列表,但不修改它,因此它可以是协变的,并且在这种情况下只是按预期工作。
答案 1 :(得分:8)
信不信由你,这不是类型安全的。
请考虑以下代码:
List<Foo> fooList = new List<Foo>();
List<IFoo> iFooList = fooList; //Illegal
iFooList.Add(new SomeOtherFoo()); //That's not Foo!
您要求进行协变转换;这对于不可变类型是唯一可能的 此外,.Net仅支持接口的协方差。
将方法更改为IEnumerable<IFoo>
,它会起作用。
答案 2 :(得分:2)
请记住,泛型类型的特化之间没有任何继承或实现关系。 List<Foo>
不以任何方式从List<IFoo>
继承。这就像将string
与int
进行比较。
这并不是说类型之间根本没有关系;只是有关的关系不是继承。相反,我们在这两种类型之间存在 专业化 关系。 .Net 4.0及更高版本中的某些类型支持称为协方差的专门化属性。在某些情况下,Co- and contra- variance允许泛型类型的特殊部分改变(在那里注明根词)。 List<T>
不是其中之一。
但是在这种情况下我会做什么(我不确定你有.Net 4.0)会让整个方法变得通用:
public void MyMethod<T>(IEnumerable<T> items) where T : IFoo
{
//...
}
请注意,我还使用了IEnumerable<T>
而不是List<T>
。您仍然可以将列表传递给此函数,因为IEnumerable<T>
和List<T>
之间存在继承/实现关系。您还可以处理数组和任何其他支持的序列。 IEnumerable也是支持协方差的类型之一。几乎任何需要List<T>
参数的方法都应该更新为使用IEnumerable<T>
。
答案 3 :(得分:0)
您需要创建一个新的List<IFoo>
并将第一个列表中的所有项目添加到其中。
答案 4 :(得分:0)
好吧,您可以将其转换为声明为List<IFoo>
的新列表,这就是您所需要的:
List<Foo> list = new List<Foo>();
theMethodThatTakesIFooList(list.ToList<IFoo>());
我猜这会有效...因为Foo可以转换为IFoo,而ToList会返回新的specefied类型列表。