无法将[]的索引应用于类型'System.Collections.Generic.IEnumerable <>的表达式


73

是否有任何特定原因为什么IEnumerable中不允许索引。

我找到了解决该问题的方法,但只是想知道为什么它不允许索引。

谢谢,

Answers:


76

因为不是。

索引涵盖在中IListIEnumerable表示“我拥有IList的某些功能,但不是全部。”

某些集合(如链接列表)无法以实际方式建立索引。但是可以逐项访问它们。 IEnumerable适用于此类收藏。请注意,集合可以实现IList和IEnumerable(以及许多其他实现)。通常,您只能找到IEnumerable一个函数参数,这意味着该函数可以接受任何种类的集合,因为它所需要的只是最简单的访问模式。


实际上,这似乎取决于情况。可以为msdn.microsoft.com/zh-cn/library/office/…的某些集合建立索引。当然,您是对的,不能从类型上进行假设,这会使它变得更加粗糙和令人作呕
nicolas

@nicolas:在这种情况下,Names它不是类,而是扩展IEnumerable接口以允许建立索引的接口,就像IList扩展IEnumerable一样。
詹姆斯·柯伦

它必须是真的,因为在COM世界中,可以建立新的接口,并为其命名惯例,但是我们真的可以将其称为接口吗?
nicolas 2014年

52

IEnumerable<T>接口不包含索引器,您可能会将其与IList<T>

如果对象确实是IList<T>(例如List<T>或array T[]),请尝试也对其类型进行引用IList<T>

否则,您可以使用myEnumerable.ElementAt(index) 使用Enumerable.ElementAt扩展方法的方法。这应该适用于所有人IEnumerable<T>。请注意,除非(运行时)对象实现IList<T>,否则这将导致index + 1枚举所有前一项,除最后一项以外的所有项都将被丢弃。

编辑:作为说明,IEnumerable<T>它只是一个表示“公开枚举数的接口”。一个具体的实现很可能是某种内存列表,它确实允许按索引快速访问,也可能不允许。例如,它可能是一个不能有效满足此类查询的集合,例如链表(James Curran提到)。它甚至可能根本不是一种内存中的数据结构,例如迭代器(根据需要生成(“屈服”)项目的)迭代器,或由从某个远程数据源获取项目的枚举器。由于IEnumerable<T>必须支持所有这些情况,因此将索引器从其定义中排除。


请注意,由于ElementAt是IEnumerable <T>的扩展方法,因此它必须一次遍历一个项目才能到达您要的项目。它可能有用,但可能会影响性能。
chilltemp

1
@chilltemp:我提到了这一点。
阿妮2010年

2
值得注意的是,的实现ElementAt足够智能,可以检查对象是否可以强制转换为对象IList,如果可以,则直接索引而不是逐项遍历。
优厚

myEnumerable.ElementAt(index)帮助了我。谢谢!
丹尼尔·利特尔

我想这也是IOrderedEnumerable<T>为什么吗?
Calculuswhiz

11

您可以使用ToList转换为列表。例如,

SomeItems.ToList()[1]

6

如果您的可枚举类型是如下所示的字符串,则可以使用索引

((string[])MyEnumerableStringList)[0]

3

原因之一可能是IEnumerable可能包含未知数量的项目。某些实现会在您对其进行迭代时生成项目列表(请参阅参考资料yield中的示例)。这对于使用索引访问项目不是很好。这将要求您知道列表中至少有那么多项目。


2

[] -operator解析为access属性this[sometype index],其实现取决于Element- Collection

一个Enumerable-Interface声明一个Collection首先应该是什么样的蓝图。

以这个例子来演示干净的接口分离的有用性:

var ienu = "13;37".Split(';').Select(int.Parse);
//provides an WhereSelectArrayIterator
var inta = "13;37".Split(';').Select(int.Parse).ToArray()[0];
//>13
//inta.GetType(): System.Int32

还要看[] -operator语法

  //example
public class SomeCollection{
public SomeCollection(){}

 private bool[] bools;

  public bool this[int index] {
     get {
        if ( index < 0 || index >= bools.Length ){
           //... Out of range index Exception
        }
        return bools[index];
     }
     set {
        bools[index] = value;
     }
  }
//...
}

投票率较高的答案可能是正确的,但我发现此评论在我自己的情况下会更有帮助(看起来与原始帖子没有什么不同)。谢谢!
atjoedonahue

1

接口的概念通常是公开一种基线协定,通过它可以保证在对象上执行工作的代码具有该对象提供的某些功能。在的情况下IEnumerable<T>,该合同恰好是“您可以逐一访问我的所有元素”。

可以单独基于此合同编写的方法种类很多。见Enumerable万吨的例子。

但是,仅将其中一项具体化为零:请考虑Sum。为了总结一堆物品,您需要什么?您需要什么合同?答案很简单:仅是查看所有项目的一种方式,仅此而已。随机访问不是必需的。甚至不需要所有项目的总数。

IEnumerable<T>接口上添加索引器将对两种方式造成不利影响:

  1. 需要上述协定(访问元素序列)的代码,如果需要IEnumerable<T>接口,将是人为限制的,因为它不能处理没有实现索引器的任何类型,即使处理这种类型也应该如此确实在代码功能范围内。
  2. 该想露出元素的序列,但没有适当的装备以提供由索引随机访问的任何类型的(例如,LinkedList<T>Dictionary<TKey, TValue>)将现在必须要么提供的一些效率低下手段模拟索引,否则放弃IEnumerable<T>接口。

综上所述,考虑到接口的目的是为了保证给定场景中所需最低功能,我确实认为IList<T>接口设计不良。或更确切地说,我认为BCL中缺少IEnumerable<T>和”之间的接口IList<T>(随机访问,但没有修改),这是一个不幸的疏忽。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.