扩展 GetEnumerator
支持 foreach
循环。
注意
本文是特性规范。 此规范是功能的设计文档。 它包括建议的规范变更,以及功能设计和开发过程中所需的信息。 这些文章将持续发布,直至建议的规范变更最终确定并纳入当前的 ECMA 规范。
功能规范与已完成的实现之间可能存在一些差异。 这些差异已记录在相关的 语言设计会议(LDM)笔记中。
可以在有关规范的文章中了解更多有关将功能规范子块纳入 C# 语言标准的过程。
支持者问题:https://github.com/dotnet/csharplang/issues/3194
总结
允许 foreach
循环识别扩展方法GetEnumerator
方法满足 foreach 模式,并在可能出现错误时对表达式进行迭代处理。
动机
这将使 foreach
与 C# 中其他功能的实现方式保持一致,包括异步和基于模式的析构。
详细设计
规范更改相对简单。 我们将 The foreach statement
§13.9.5 节修改为以下文本:
foreach 语句的编译时处理首先确定表达式的 集合类型、枚举器类型 和 元素类型。 该确定过程如下:
如果
X
的类型 是数组类型,那么从X
到IEnumerable
接口会有隐式引用转换,因为System.Array
实现了该接口。 集合类型是IEnumerable
接口,枚举器类型 是IEnumerator
接口,元素类型是数组类型X
的元素类型。如果表达式的类型
X
为dynamic
,则存在从表达式到IEnumerable
接口的隐式转换 (§10.2.10)。 集合类型是IEnumerable
接口,枚举器类型是IEnumerator
接口。 如果var
标识符作为 local_variable_type 提供,则 元素类型 是dynamic
,否则是object
。否则,请确定类型
X
是否具有适当的GetEnumerator
方法:
- 对标识符为
GetEnumerator
且没有类型参数的类型X
执行成员查找。 如果成员查找未找到匹配项,或找到不明确的匹配项,或找到的匹配项不是方法组,请按如下所述检查可枚举接口。 建议在成员查找产生除方法组之外的任何结果或没有匹配项时发出警告。- 使用生成的方法组和空参数列表执行重载解析。 如果重载解析的结果是没有适用的方法,产生歧义或仅有一个最佳方法,但该方法是静态的或非公开的,请检查是否具有如下所述的可枚举接口。 如果重载解析产生除明确的公共实例方法之外的任何结果,或者没有适用的方法,建议发出警告。
- 如果
GetEnumerator
方法的返回类型E
不是类、结构或接口类型,则会生成错误,并且不会采取进一步的步骤。- 对标识符为
Current
且没有类型参数的E
执行成员查找。 如果成员查找未找到匹配项,则结果为错误;或者结果是除允许读取的公共实例属性之外的任何内容,则会生成错误,并且不执行进一步的步骤。- 对标识符为
MoveNext
且没有类型参数的E
执行成员查找。 如果成员查找不生成匹配项,则结果为错误;或者结果为除方法组之外的任何内容,则会生成错误,并且不执行进一步的步骤。- 重载解析过程是在具有空参数列表的方法组上执行的。 如果重载解析导致没有适用的方法、产生歧义,或只有单一最佳方法,但该方法是静态的或不公开的,或者其返回类型不是
bool
,则会产生错误,并且不执行进一步的步骤。- 集合类型是
X
,枚举器类型是E
,元素类型是Current
属性的类型。否则,检查可枚举的接口:
- 如果在所有类型
Ti
中有从X
到IEnumerable<Ti>
的隐式转换,则存在唯一类型T
,使得T
不是dynamic
,而对于所有其他Ti
,有从IEnumerable<T>
到IEnumerable<Ti>
的隐式转换,那么集合类型是接口IEnumerable<T>
,枚举器类型是接口IEnumerator<T>
,元素类型是T
。- 否则,如果有多个此类类型
T
,则会生成错误,并且不采取进一步的步骤。- 否则,如果存在从
X
到System.Collections.IEnumerable
接口的隐式转换,则集合类型是此接口,则枚举器类型是接口System.Collections.IEnumerator
,元素类型是object
。否则,请确定类型“X”是否具有适当的
GetEnumerator
扩展方法:
- 对标识符为
X
的类型GetEnumerator
执行扩展方法查找。 如果成员查找不生成匹配项,或者生成不明确的匹配项,或者生成不是方法组的匹配项,则会生成错误,并且不执行进一步的步骤。 建议在成员查找产生除方法组之外的任何结果或没有匹配项时发出警告。- 使用生成的方法组和类型
X
的单个参数执行重载解析。 如果重载解析没有产生任何适用的方法,导致歧义,或得出一个单一的最佳方法但该方法不可访问,那么将产生一个错误,并且不会进行进一步的步骤。
- 如果
X
是结构类型,则此决议允许第一个参数通过 ref 传递,并且ref类型是in
。- 如果
GetEnumerator
方法的返回类型E
不是类、结构或接口类型,则会生成错误,并且不会采取进一步的步骤。- 对标识符为
Current
且没有类型参数的E
执行成员查找。 如果成员查找未找到匹配项,则结果为错误;或者结果是除允许读取的公共实例属性之外的任何内容,则会生成错误,并且不执行进一步的步骤。- 对标识符为
MoveNext
且没有类型参数的E
执行成员查找。 如果成员查找不生成匹配项,则结果为错误;或者结果为除方法组之外的任何内容,则会生成错误,并且不执行进一步的步骤。- 重载解析过程是在具有空参数列表的方法组上执行的。 如果重载解析导致没有适用的方法、产生歧义,或只有单一最佳方法,但该方法是静态的或不公开的,或者其返回类型不是
bool
,则会产生错误,并且不执行进一步的步骤。- 集合类型是
X
,枚举器类型是E
,元素类型是Current
属性的类型。否则,将生成错误,且不再执行任何步骤。
对于 await foreach
,规则也进行了类似的修改。 该规范所需的唯一更改是将说明中的 Extension methods do not contribute.
行删除,因为该规范的其余部分基于上述规则,其中将不同名称替换为模式方法。
缺点
每一项更改都会增加语言的复杂性,这可能会使那些未被设计为 foreach
的事物被 foreach
,例如 Range
。
替代方案
不执行任何操作。
未解决的问题
目前没有。