为什么我不能将具体类型列表分配给该具体界面的列表?

为什么这不编译?

public interface IConcrete { } public class Concrete : IConcrete { } public class Runner { public static void Main() { var myList = new List(); DoStuffWithInterfaceList(myList); // compiler doesn't allow this } public static void DoStuffWithInterfaceList(List listOfInterfaces) { } } 

什么是将myList设置为正确类型的最快方法?

编辑我弄乱了DoStuffWithInterfaceList示例

对于大型列表而言,接受的解决方案效率很低,而且完全没有必要。 您可以稍微更改方法的签名,以使代码无需任何隐式或显式转换即可运行:

 public class Runner { public static void Main() { var myList = new List(); DoStuffWithInterfaceList(myList); // compiler doesn't allow this } public static void DoStuffWithInterfaceList(List listOfInterfaces) where T: IConcrete { } } 

请注意,该方法现在是通用的,并使用类型约束来确保只能使用IConcrete子类型列表调用它。

几乎所有这些答案都说这将在C#4中得到支持。他们都错了。

简单来说: 这不是我们将在C#4中支持的协方差的一个例子,因为这样做不会是类型安全的。 我们支持使用引用类型参数构造的generics接口和委托的类型安全协方差和逆变 。 这里的示例使用类类型List,而不是接口类型。 并且接口类型IList对于协方差或逆变不是类型安全的。

IEnumerable将是协变的,因为它是一个对协方差安全的接口。

目前,这是禁止的,否则类型安全将被打破。 你可以在DoStuffWithInterfaceList中做这样的事情:

 public class OtherConcrete : IConcrete { } public void DoStuffWithInterfaceList(List listOfInterfaces) { listOfInterfaces.Add(new OtherConcrete ()); } 

哪个会在运行时失败,因为listOfInterfaces只是Concrete类型。

正如其他人所说,只要您不更改方法内的列表,就可以使用C#4,但是您必须明确告诉编译器。

要回答有关转换列表的其他问题,如果您使用.Net 3.5,我将使用Enumerable.Cast <>扩展方法。 否则,你可以使用yield关键字自己编写一个惰性转换方法,这将给你相同的效果。

编辑:

正如Eric Lippert所说,你应该使用IEnumerable来使它在C#4中工作。

这与协方差和逆变有关。 Eric Lippert在今年早些时候写了很多关于它的文章。 (11篇专门针对该主题的博客文章。)第一篇是C#中的协方差和逆变,第一部分 。 阅读并阅读他的博客,了解其余部分。 他详细解释了为什么这种事情很困难。

好消息:C#4.0中解除了一些限制。

IList不起作用,因为IList不是逆变的。 它需要是IEnumerable,但这只适用于4.0。 您也可以使用带有lambda表达式的ConvertAll,它将在3.5中工作

C#当前不支持转换这样的generics类型( 如果我理解正确的话,它将在C#4中得到支持正如wcoenen在下面的评论中所述,并且Eric在他的回答中也澄清了,使其在C#4中工作的唯一方法是使用IEnumerable 。 现在,您需要以某种方式转换列表。

您可以像这样调用方法:

 DoStuffWithInterface(myList.ConvertAll(n => n as IConcrete)); 

更新
我意识到你可能不需要在lambda中使用cast,尽管为了清晰起见我有点喜欢它。 所以这也应该有效:

 DoStuffWithInterface(myList.ConvertAll(n => n)); 

你可以试试

 public void DoStuffWithInterface(IList concrete) { } 

但我认为这只适用于.NET 4.0。

如果你想变脏,那就去做吧

 public void DoStuffWithInterface(IList concrete) { } 

并检查出来的物体是否具体。

 foreach (var item in myList) DoStuffWithInterface(item); 

要么

 public void DoStuffWithInterface(IList concrete) { } 

要么

 var myNewList = myList.Cast();