![](/img/trans.png)
[英]Why can't I add concrete classes to a list of interfaces which the concrete classes implement?
[英]Why can I not assign a List of concrete types to a List of that concrete's interface?
为什么这不编译?
public interface IConcrete { }
public class Concrete : IConcrete { }
public class Runner
{
public static void Main()
{
var myList = new List<Concrete>();
DoStuffWithInterfaceList(myList); // compiler doesn't allow this
}
public static void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces) { }
}
什么是将myList设置为正确类型的最快方法?
编辑我弄乱了DoStuffWithInterfaceList示例
几乎所有这些答案都说这将在C#4中得到支持。他们都错了。
简单来说: 这不是我们将在C#4中支持的协方差的一个例子,因为这样做不会是类型安全的。 我们支持使用引用类型参数构造的泛型接口和委托的类型安全协方差和逆变 。 这里的示例使用类类型List,而不是接口类型。 并且接口类型IList对于协方差或逆变不是类型安全的。
IEnumerable将是协变的,因为它是一个对协方差安全的接口。
对于大型列表,接受的解决方案效率非常低,而且完全没有必要。 您可以稍微更改方法的签名,以使代码无需任何隐式或显式转换即可运行:
public class Runner
{
public static void Main()
{
var myList = new List<Concrete>();
DoStuffWithInterfaceList(myList); // compiler doesn't allow this
}
public static void DoStuffWithInterfaceList<T>(List<T> listOfInterfaces)
where T: IConcrete
{ }
}
请注意,该方法现在是通用的,并使用类型约束来确保只能使用IConcrete
子类型列表调用它。
目前,这是禁止的,否则类型安全将被打破。 你可以在DoStuffWithInterfaceList中做这样的事情:
public class OtherConcrete : IConcrete { }
public void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces)
{
listOfInterfaces.Add(new OtherConcrete ());
}
哪个会在运行时失败,因为listOfInterfaces只是Concrete类型。
正如其他人所说,只要您不更改方法内的列表,就可以使用C#4,但是您必须明确告诉编译器。
要回答有关转换列表的其他问题,如果您使用的是.Net 3.5,我将使用Enumerable.Cast <>扩展方法。 否则,你可以使用yield关键字自己编写一个惰性转换方法,这将给你相同的效果。
编辑:
正如Eric Lippert所说,你应该使用IEnumerable来使它在C#4中工作。
C#目前不支持转换类似的泛型类型(
如果我理解正确的话,它将在C#4中得到支持
正如wcoenen在下面的评论中所述,并且Eric在他的回答中也澄清了,使它在C#4中工作的唯一方法是使用IEnumerable<IConcrete>
。 现在,您需要以某种方式转换列表。
您可以像这样调用方法:
DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n as IConcrete));
更新
我意识到你可能不需要在lambda中使用cast,尽管为了清晰起见我有点喜欢它。 所以这也应该有效:
DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n));
这与协方差和逆变有关。 Eric Lippert在今年早些时候写了很多关于它的文章。 (11篇专门针对该主题的博客文章。)第一篇是C#中的协方差和逆变,第一部分 。 阅读并阅读他的博客,了解其余部分。 他详细解释了为什么这种事情很困难。
好消息:C#4.0中解除了一些限制。
IList不起作用,因为IList不是逆变的。 它需要是IEnumerable,但这只适用于4.0。 您也可以使用带有lambda表达式的ConvertAll,它将在3.5中工作
你可以试试
public void DoStuffWithInterface(IList<IConcrete> concrete) { }
但我认为这只适用于.NET 4.0。
如果你想变脏,就去做吧
public void DoStuffWithInterface(IList concrete) { }
并检查出来的物体是否具体。
foreach (var item in myList)
DoStuffWithInterface(item);
要么
public void DoStuffWithInterface(IList<IConcrete> concrete) { }
要么
var myNewList = myList.Cast<IConcrete>();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.