2

我想知道,为什么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>...

我在这里错过了什么吗?

4

6 回答 6

4

你不能插入到IEnumerable. 不幸的是,没有“IInsertable”。

没有界面告诉您只能将项目添加到集合中。这将是一个in泛型类型。因为您可以在每个集合接口上获取项目,所以不可能有in通用参数。

编辑

该接口IEnumerable<T>只有 getter,因此您不能使用该接口向集合中插入任何元素。因此,out在 的声明中是允许的IEnumerable<out T>。它告诉编译器,类型T的值只在一个方向上,正在“出来”。这使它在一个方向上兼容。您可以IEnumerable<string>用作IEnumerable<object>,因为您只是从中读取。您可以将字符串读取为对象。但是你不能写入它,你不能将对象作为字符串传递。它可能是整数或任何其他类型。

于 2012-05-07T08:46:56.690 回答
4

在介绍 C# 中的协/逆变之前, Eric Lippert 有一系列关于此的帖子,值得一读。

一个简单的例子是

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>. 所有的长颈鹿都是动物,但不是所有的动物都是长颈鹿

于 2012-05-07T08:58:09.260 回答
2

我在这里看不到你的问题。使用时IEnumerable你不能插入,那不是你在做什么。您正在尝试做的是分配IEnumerable<string>一个值,IEnumerable<object>这是行不通的。当您为您分配IEnumerable<string>一个类型的对象时,IEnumerble<object>您不能保证列表中的所有对象都是字符串类型。这就是为什么不能这样做。

协变,out关键字,interfaces 确保输出总是可以被消费部分理解,也就是说,IEnumerable<string>可以将 an 转换为IEnumerable<object>since列表,string : object并且消费列表的人object必须知道如何处理 a string,但不能反过来。

逆变,in关键字,接口使您能够将对象转换为较少指定的泛型,也就是说,IComparer<object>可以转换为,IComparer<string>因为实现的类IComparer<object>现在必须如何处理string,因为它是逆变的,但不是相反。

在此处阅读更多信息:http: //msdn.microsoft.com/en-us/library/dd799517.aspx

于 2012-05-07T09:03:57.180 回答
1

我认为输入安全性是严格意义上的:如果你在你的列表中
IEnumerable<object> {"Hello", 2.34d, new MyCoolObject(), enumvalue}怎么办?

运行时应该如何处理这些转换?

如果string它可能是一个简单的案例,因为它应该足以调用覆盖(如果有的话)ToString(),但框架设计者不能依赖自定义案例并且应该提供通用解决方案。

希望这可以帮助。

于 2012-05-07T08:46:44.600 回答
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;
}

希望这可以帮助。

于 2012-05-07T09:19:53.817 回答
0

您不能分配给IEnumerable<Parent>IEnumerable<Child>因为它可能包含不是 Child.

但是如果我想使用 IEnumerable 作为插入呢?就像是 :

IEnumerable<object> objects = ...;
IEnumerable<string> strings = objects

在编译时无法知道IEnumerable<object>不包含非string. 但是,如果您(程序员)知道那里只有strings,您可以在运行时进行强制转换:

IEnumerable<string> strings = objects.Cast<string>();

顺便说一句,您可能想看看标签描述

于 2012-05-07T12:18:07.353 回答