无法将带有 [] 的索引应用于“System.Collections.Generic.IEnumerable<> 类型的表达式
Posted
技术标签:
【中文标题】无法将带有 [] 的索引应用于“System.Collections.Generic.IEnumerable<> 类型的表达式【英文标题】:Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.IEnumerable<> 【发布时间】:2011-04-06 11:28:05 【问题描述】:IEnumerable 中不允许索引是否有任何具体原因。
我找到了解决问题的方法,但只是想知道为什么它不允许索引。
谢谢,
【问题讨论】:
【参考方案1】:因为不是。
索引由IList
覆盖。 IEnumerable
的意思是“我拥有 IList 的一些权力,但不是全部。”
某些集合(如链表)无法以实用的方式建立索引。但是可以逐项访问它们。 IEnumerable
用于这样的集合。请注意,集合可以同时实现 IList 和 IEnumerable(以及许多其他)。你一般只会找到IEnumerable
作为函数参数,这意味着函数可以接受任何类型的集合,因为它只需要最简单的访问模式。
【讨论】:
实际上,这似乎有点取决于。一些集合msdn.microsoft.com/en-us/library/office/… 可以被索引。当然你是对的,这不能从类型上假设,这使得它更加恶心和恶心 @nicolas:在这种情况下,Names
不是一个类,而是一个扩展 IEnumerable 接口以允许索引的接口,就像 IList 扩展 IEnumerable 一样。
这一定是真的,因为在 COM 世界中,我们可以创建新的接口,并放弃它们的命名约定,但我们真的可以称它为接口吗?【参考方案2】:
IEnumerable<T>
接口不包含indexer,您可能会将其与IList<T>
混淆
如果对象确实是IList<T>
(例如List<T>
或数组T[]
),请尝试将其引用类型为IList<T>
。
否则,您可以使用myEnumerable.ElementAt(index)
,它使用Enumerable.ElementAt 扩展方法。这应该适用于所有 IEnumerable<T>
s 。
请注意,除非(运行时)对象实现IList<T>
,否则这将导致所有第一个index + 1
项目被枚举,除了最后一个被丢弃。
编辑:
作为解释,IEnumerable<T>
只是一个表示“暴露枚举器的接口”的接口。一个具体的实现很可能是某种内存列表,确实允许按索引快速访问,也可能不允许。例如,它可能是无法有效满足此类查询的集合,例如链表(如 James Curran 所述)。它甚至可能完全是没有分类的内存中数据结构,例如迭代器,其中项目是按需生成('yielded')的,或者由从某些获取项目的枚举器远程数据源。因为IEnumerable<T>
必须支持所有这些情况,所以索引器被排除在其定义之外。
【讨论】:
请注意,由于 ElementAt 是 IEnumerableElementAt
的实现足够智能,可以检查对象是否可以转换为IList
,如果可以直接索引而不是逐项遍历。
myEnumerable.ElementAt(index) 帮助了我。谢谢!【参考方案3】:
您可以使用 ToList 转换为列表。例如,
SomeItems.ToList()[1]
【讨论】:
【参考方案4】:如果您的可枚举类型是如下字符串,则可以使用索引
((string[])MyEnumerableStringList)[0]
【讨论】:
【参考方案5】:一个原因可能是IEnumerable
可能包含未知数量的项目。某些实现会在您对其进行迭代时生成项目列表(有关示例,请参阅yield
)。这在使用索引访问项目时效果不佳。这将要求您知道列表中至少有那么多项目。
【讨论】:
【参考方案6】:[]-操作符被解析为访问属性this[sometype index]
,其实现取决于元素-集合。
Enumerable-Interface 首先声明了一个集合应该是什么样子的蓝图。
以这个例子来说明干净的界面分离的用处:
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
同时查看[]-运算符的语法:
//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;
//...
【讨论】:
投票率较高的答案可能是正确的,但我发现此评论在我自己的场景中更有帮助(与原始帖子似乎没有不同)。谢谢!【参考方案7】:接口的想法通常是公开一种基线契约,通过该契约可以保证在对象上执行工作的代码可以保证该对象提供的某些功能。在IEnumerable<T>
的情况下,该合同恰好是“您可以一个一个地访问我的所有元素。”
仅基于这个契约可以编写的方法种类很多。请参阅 Enumerable
类以获取 吨 示例。
但要在一个具体的问题上归零:想想Sum
。为了总结一堆项目,你需要什么?你需要什么合同?答案很简单:只是一种查看每个项目的方法,仅此而已。不需要随机访问。甚至不需要所有项目的总数。
向IEnumerable<T>
接口添加索引器会在两个方面造成不利影响:
-
需要上述协定(访问一系列元素)的代码,如果它需要
IEnumerable<T>
接口,将被人为限制,因为它无法处理任何未实现索引器的类型,即使处理这种类型应该确实在代码的能力范围内。
任何想要公开一系列元素但没有适当装备以提供按索引随机访问的类型(例如,LinkedList<T>
、Dictionary<TKey, TValue>
)现在必须提供一些低效的模拟方法 索引,否则放弃IEnumerable<T>
接口。
话虽如此,考虑到接口的目的是保证在给定场景中所需的最低功能,我真的认为IList<T>
接口设计得很糟糕。或者更确切地说,在我看来,缺少 an interface "between" IEnumerable<T>
and IList<T>
(随机访问,但没有修改)是 BCL 中的一个不幸的疏忽。
【讨论】:
【参考方案8】:我有一列不允许空值,我正在插入空值。
【讨论】:
【参考方案9】:您可以将IEnumerable
与this
结合使用:
public interface IWorksheets : IEnumerable
IWorksheet this[int index] get;
IWorksheet this[string name] get;
然后你可以使用foreach
和索引:
IWorksheet worksheet= excelWorkbook.Worksheets["Sheet1"]
foreach (IWorksheet worksheet in excelWorkbook.Worksheets)
【讨论】:
以上是关于无法将带有 [] 的索引应用于“System.Collections.Generic.IEnumerable<> 类型的表达式的主要内容,如果未能解决你的问题,请参考以下文章
无法使用 [] 将索引应用于“System.Dynamic.DynamicObject”类型的表达式