我想知道,IEnumerable<T>
为什么只有out
而不是in
逆变旗?
public interface IEnumerable<out T>
我可以通过这个例子理解out
:
IEnumerable<string> strings = new List<string>(){"a","b","c"};
IEnumerable<object> objects = strings;
object
大于string
,因此编译器担心会做类似的事情:
objects = objects.First().Select(x=>5);// ( we cant insert int to string...)
很好并且理解。
但如果我想将IEnumerable用作插入呢?
类似的东西:
IEnumerable<object> objects = ...;
IEnumerable<string> strings = objects
所以我也可以插入对象......
但问题是没有IEnumerable<in T>
...
答案 0 :(得分:4)
您无法插入IEnumerable
。不幸的是,没有“IInsertable”。
没有界面可以告诉您只能将项目添加到集合中。这将是in
泛型类型。因为您可以在每个集合接口上获取项目,所以不可能有in
个泛型参数。
修改强>
接口IEnumerable<T>
只有getter,因此您无法使用该接口向集合中插入任何元素。因此,out
的声明中允许IEnumerable<out T>
。它告诉编译器,类型T
的值只是朝着一个方向,即“出来”。这使它在一个方向上兼容。您可以将IEnumerable<string>
用作IEnumerable<object>
,因为您只是从中读取。您可以将字符串作为对象读取。但是你无法写入它,你无法将对象作为字符串传递。它可能是整数或任何其他类型。
答案 1 :(得分:4)
Eric Lippert在介绍C#中的co / contravariance之前有一个series of posts,值得一读。
一个简单的例子就是
public class Animal(){
...
}
public class Giraf(){
public void LookAboveTheClouds(){}
}
IEnumerable<Animal> animals = new list<Animal>{
new Elephant(),
new Tiger(),
new TinyMouse()};
IEnumerable<Giraf> girafs = animals;
foreach(var giraf in girafs){
//elephants,tigers and tiny mice does not suuport this
giraf.LookAboveTheClouds()
}
换句话说,编译器无法保证您可以安全地将IEnumerable<Animal>
用作IEnumerable<Giraf>
。所有的长颈鹿都是动物,但不是所有的动物都是长颈鹿
答案 2 :(得分:2)
我在这里看不到你的问题。使用IEnumerable
时无法插入,这不是您正在做的事情。您要做的是分配IEnumerable<string>
值为IEnumerable<object>
但不起作用。当您为IEnumerable<string>
分配类型为IEnumerble<object>
的对象时,您无法保证列表中的所有对象都是字符串类型。这就是为什么不能这样做。
Covariant,out
关键字,接口确保消费部分始终可以理解输出,即IEnumerable<string>
可以被转换为IEnumerable<object>
的列表,因为{ {1}}而使用string : object
列表的人必须知道如何处理object
,而不是相反。
Contravariant,string
关键字接口使您可以将对象转换为较少指定的泛型,也就是说,in
可以转换为IComparer<object>
,因为实现IComparer<string>
的类现在必须如何处理IComparer<object>
因为它是逆变而不是反过来。
在此处阅读更多内容:http://msdn.microsoft.com/en-us/library/dd799517.aspx
答案 3 :(得分:1)
我认为输入安全性是严格意义的:如果你在列表中怎么办?
IEnumerable<object> {"Hello", 2.34d, new MyCoolObject(), enumvalue}
?
运行时应如何处理这些转换?
如果string
可能是一个简单的情况,因为它应该足以调用ToString()
的覆盖(如果有的话),但框架设计师不能依赖关于定制案例,应提供通用解决方案。
希望这有帮助。
答案 4 :(得分:1)
正如评论中已经提到的,您不能使用IEnumerable来修改集合。
由于您提到的类型安全问题(将int插入到字符串列表中),编译器将不允许类似下面的代码,因为您可以使用对象修改字符串列表
IEnumerable只是集合上的迭代器。
static void Main(string[] args)
{
IList<string> strings = new List<string> { "a", "b", "c" };
IList<object> objects = strings; ** // This will give an error because compiler does not want you to do the line below **
objects.Add(5); // Type safety violated for IList<string> hence not allowed
// The code below works fine as you cannot modify the strings collection using IEnumerable of object
//IEnumerable<string> strings = new List<string> { "a", "b", "c" };
//IEnumerable<object> objects = strings;
}
希望这有帮助。
答案 5 :(得分:0)
您无法将IEnumerable<Parent>
分配给IEnumerable<Child>
,因为它可能包含不 Child
的元素。
但如果我想将IEnumerable用作插入呢?类似的东西:
IEnumerable<object> objects = ...;
IEnumerable<string> strings = objects
在编译时无法知道 IEnumerable<object>
不包含非string
。但是,如果您(程序员)知道那里只有string
,那么您可以在运行时进行转换:
IEnumerable<string> strings = objects.Cast<string>();
顺便说一下,您可能需要查看标记description的covariance。