2017-07-27 106 views
41

我有一个设置是这样的:无法从列表<Bar>转换到列表<Foo>

abstract class Foo {} 
class Bar : Foo {} 

这种形式的其他地方的方法:

void AddEntries(List<Foo>) {} 

我试图用调用此方法对象列表Bar

List<Bar> barList = new List<Bar>() 
AddEntries(barList); 

但这给了我错误:

cannot convert from List<Bar> to List<Foo>

反正有这个问题吗?我需要使用抽象类来保持方法定义。

+1

C#有类似语法'名单<?扩展Foo>'就像Java一样? – dbush

+2

@dbush是的,C#的版本是在接受的答案 – Harrichael

+4

@harrichael虽然不像Java,C#支持泛型协变,所以有一个更好的选择(见罗布的回答) –

回答

64

你可以让你的AddEntries通用的,它改变这个

void AddEntries<T>(List<T> test) where T : Foo 
{ 
    //your logic 
} 

看一看Constraints on Type Parameters进一步的信息。

60

请仔细阅读Covariance and Contravariance in Generics from docs.microsoft.com,尤其是“与协变类型参数的通用接口”将覆盖您正在使用的场景。

总之,如果你(能)你AddEntries方法的改变签名:

static void AddEntries(IEnumerable<Foo> entries) 

(注意使用的IEnumerable<Foo>代替List<Foo>),你就可以做你要找什么去做。

的具体位置不同的是,IEnumerable<T>List<T>声明不同:

public interface IEnumerable<out T> : IEnumerable 
public class List<T> : IList<T>, ... ... 

重要是在IEnumerable<T>声明的out这表明它是这意味着差异(其定义取自docs.microsoft.com):

Covariance: Enables you to use a more derived type than originally specified.

+1

考虑到方法名称是AddEntries,OP不太可能使用IEnumerable。 – Taemyr

+0

@Taemyr然而,OP可以定义一个'interface'。 – wizzwizz4

+7

@Taemyr:我假设'AddEntries'的意思是“将这些条目添加到其他列表”,而不是“添加一些条目到这个列表”。否则这两个答案都不适用于他,因为无论如何,他都无法将“FooChild”添加到“列表”。 –

43

这是不允许的,原因很简单。假设下面的编译:

AddEntries(new List<Bar>()); 

void AddEntries(List<Foo> list) 
{ 
    // list is a `List<Bar>` at run-time 
    list.Add(new SomethingElseDerivingFromFoo()); // what ?! 
} 

如果你的代码将编译,你有,你加SomethingElseDerivingFromFoo实际上一个List<Bar>(运行时类型)不安全的加法(这使得整个泛型列表毫无意义)。

解决你的问题,你可以使用泛型:

void AddEntries<T>(List<T> list) where T:Foo 
{ 

} 
+3

竖起大拇指为解释*为什么*你不能做演员,而不是只显示一种方法,使其工作。 –

+0

我不明白。这是[完全有效的代码](http://ideone.com/xoNJX8)。你不能依赖派生类中提供的任何附加功能(没有明确的转换)。 – Phylogenesis

+1

@Phylogenesis你不能将'List '传递给'AddEntries',因为如果你在这个列表中添加了'SomethingElseDerivingFromFoo',这将是一种奇怪的行为(你在List中有'SomethingElseDerivingFromFoo'项目''。代码我张贴完美的编译 – user3185569

相关问题