无法将List <foo>传递给期望List <ifoo>的方法,其中Foo:IFoo </ifoo> </foo>

时间:2011-08-01 17:34:23

标签: c# list generics interface casting

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

我如何解决这个问题,为什么会发生这种情况? (总是很好地从错误中吸取教训)

5 个答案:

答案 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>继承。这就像将stringint进行比较。

这并不是说类型之间根本没有关系;只是有关的关系不是继承。相反,我们在这两种类型之间存在 专业化 关系。 .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类型列表。