扩展 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 的类型 是数组类型,那么从 XIEnumerable 接口会有隐式引用转换,因为 System.Array 实现了该接口。 集合类型IEnumerable 接口,枚举器类型IEnumerator 接口,元素类型是数组类型 X 的元素类型。

  • 如果表达式的类型 Xdynamic,则存在从表达式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 中有从 XIEnumerable<Ti> 的隐式转换,则存在唯一类型 T,使得 T 不是 dynamic,而对于所有其他 Ti,有从 IEnumerable<T>IEnumerable<Ti> 的隐式转换,那么集合类型是接口 IEnumerable<T>枚举器类型是接口 IEnumerator<T>元素类型T
    • 否则,如果有多个此类类型 T,则会生成错误,并且不采取进一步的步骤。
    • 否则,如果存在从 XSystem.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

替代方案

不执行任何操作。

未解决的问题

目前没有。