扩展 GetEnumerator 支持 foreach 循环。Extension GetEnumerator support for foreach loops.

总结Summary

允许 foreach 循环识别扩展方法 GetEnumerator 方法,该方法以其他方式满足 foreach 模式,并在该表达式被视为错误时循环使用。Allow foreach loops to recognize an extension method GetEnumerator method that otherwise satisfies the foreach pattern, and loop over the expression when it would otherwise be an error.

动机Motivation

这会在实现 c # 中的其他功能(包括异步和基于模式的析构)的情况下引入 foreach 内联。This will bring foreach inline with how other features in C# are implemented, including async and pattern-based deconstruction.

详细设计Detailed design

Spec 更改相对简单。The spec change is relatively straightforward. 修改 The foreach statement 此文本部分:We modify The foreach statement section to this text:

Foreach 语句的编译时处理首先确定表达式的 集合类型 _、 _枚举器类型*_ 和 _ *元素类型**。The compile-time processing of a foreach statement first determines the collection type _, _enumerator type_ and _ element type of the expression. 这种决定将按如下方式进行:This determination proceeds as follows:

  • 如果表达式的 X 类型是数组类型,则从到接口 (存在隐式引用转换, X IEnumerable 因为 System.Array) 实现此接口。If the type X of expression is an array type then there is an implicit reference conversion from X to the IEnumerable interface (since System.Array implements this interface). 集合类型 _ 是 IEnumerable 接口, _枚举器类型_ 为 IEnumerator 接口,_ 元素类型 是数组类型的元素类型 XThe collection type _ is the IEnumerable interface, the _enumerator type_ is the IEnumerator interface and the _ element type is the element type of the array type X.

  • 如果表达式的 X 类型为,则 dynamicexpression 到 Interface 的隐式转换 IEnumerable (隐式动态转换) 。If the type X of expression is dynamic then there is an implicit conversion from expression to the IEnumerable interface (Implicit dynamic conversions). 集合类型 _ 是 IEnumerable 接口,而 _枚举器类型*_ 是 IEnumerator 接口。The collection type _ is the IEnumerable interface and the _enumerator type*_ is the IEnumerator interface. 如果将 var 标识符作为 _local_variable_type * 提供,则 元素类型dynamic ,否则为 objectIf the var identifier is given as the _local_variable_type* then the element type is dynamic, otherwise it is object.

  • 否则,请确定该类型是否 X 具有适当的 GetEnumerator 方法:Otherwise, determine whether the type X has an appropriate GetEnumerator method:

    • X 具有标识符 GetEnumerator 且无类型参数的类型执行成员查找。Perform member lookup on the type X with identifier GetEnumerator and no type arguments. 如果成员查找不会生成匹配项,或者它产生了多义性,或者产生了不是方法组的匹配项,请检查可枚举的接口,如下所述。If the member lookup does not produce a match, or it produces an ambiguity, or produces a match that is not a method group, check for an enumerable interface as described below. 如果成员查找产生了除方法组以外的任何内容,则建议发出警告。It is recommended that a warning be issued if member lookup produces anything except a method group or no match.
    • 使用生成的方法组和空参数列表执行重载决策。Perform overload resolution using the resulting method group and an empty argument list. 如果重载决策导致没有适用的方法、导致歧义或产生单个最佳方法,但该方法是静态的或非公共的,请按如下所述检查可枚举的接口。If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, check for an enumerable interface as described below. 如果重载决策产生了除明确的公共实例方法之外的任何内容,或者没有适用方法,则建议发出警告。It is recommended that a warning be issued if overload resolution produces anything except an unambiguous public instance method or no applicable methods.
    • 如果该方法的返回类型 E GetEnumerator 不是类、结构或接口类型,则会生成错误,并且不会执行任何其他步骤。If the return type E of the GetEnumerator method is not a class, struct or interface type, an error is produced and no further steps are taken.
    • 成员查找是在上 E 用标识符 Current 而不是类型参数执行的。Member lookup is performed on E with the identifier Current and no type arguments. 如果成员查找没有生成任何匹配项,则结果为错误,或结果为除允许读取的公共实例属性之外的任何内容,并不执行任何其他步骤。If the member lookup produces no match, the result is an error, or the result is anything except a public instance property that permits reading, an error is produced and no further steps are taken.
    • 成员查找是在上 E 用标识符 MoveNext 而不是类型参数执行的。Member lookup is performed on E with the identifier MoveNext and no type arguments. 如果成员查找没有生成任何匹配项,则结果为错误,或结果为除方法组外的任何内容,并不执行任何其他步骤。If the member lookup produces no match, the result is an error, or the result is anything except a method group, an error is produced and no further steps are taken.
    • 使用空参数列表对方法组执行重载决策。Overload resolution is performed on the method group with an empty argument list. 如果重载决策导致没有适用的方法,导致不确定性,或者导致单个最佳方法,但该方法是静态的或非公共的,或者它的返回类型不是 bool ,则会生成错误,并且不会执行任何其他步骤。If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, or its return type is not bool, an error is produced and no further steps are taken.
    • 集合类型 _ 是 X , _枚举器类型_ 为 E ,_ 元素类型 是属性的类型 CurrentThe collection type _ is X, the _enumerator type_ is E, and the _ element type is the type of the Current property.
  • 否则,请检查可枚举的接口:Otherwise, check for an enumerable interface:

    • 如果 Ti 存在从到的隐式转换到的所有类型 X IEnumerable<Ti> ,则有一个唯一类型,这种类型不适用, T T dynamic 并且对于所有其他类型的 Ti 隐式转换均 IEnumerable<T>IEnumerable<Ti> ,则 集合类型 _ 是接口 IEnumerable<T> , _枚举器类型_ 是接口 IEnumerator<T> ,而 _ 元素类型TIf among all the types Ti for which there is an implicit conversion from X to IEnumerable<Ti>, there is a unique type T such that T is not dynamic and for all the other Ti there is an implicit conversion from IEnumerable<T> to IEnumerable<Ti>, then the collection type _ is the interface IEnumerable<T>, the _enumerator type_ is the interface IEnumerator<T>, and the _ element type is T.
    • 否则,如果有多个这样的类型 T ,则会生成错误,并且不会执行任何其他步骤。Otherwise, if there is more than one such type T, then an error is produced and no further steps are taken.
    • 否则,如果存在从到接口的隐式转换 X System.Collections.IEnumerable ,则 集合类型 _ 是此接口, _枚举器类型_ 是接口 System.Collections.IEnumerator ,而 _ 元素类型objectOtherwise, if there is an implicit conversion from X to the System.Collections.IEnumerable interface, then the collection type _ is this interface, the _enumerator type_ is the interface System.Collections.IEnumerator, and the _ element type is object.
  • 否则,确定类型 "X" 是否具有适当的 GetEnumerator 扩展方法:Otherwise, determine whether the type 'X' has an appropriate GetEnumerator extension method:

    • 对标识符为的类型执行扩展方法查找 X GetEnumeratorPerform extension method lookup on the type X with identifier GetEnumerator. 如果成员查找不会生成匹配项,或者它产生了多义性,或者产生了不是方法组的匹配项,则会生成错误,并且不会执行任何其他步骤。If the member lookup does not produce a match, or it produces an ambiguity, or produces a match which is not a method group, an error is produced and no further steps are taken. 如果成员查找产生了除方法组外的任何内容,或者没有匹配项,则建议发出警告。It is recommended that a warning be issues if member lookup produces anything except a method group or no match.
    • 使用生成的方法组和一个类型的参数来执行重载决策 XPerform overload resolution using the resulting method group and a single argument of type X. 如果重载决策不生成适用的方法、导致歧义,或者导致单个最佳方法,但该方法不可访问,则不会执行任何其他步骤。If overload resolution produces no applicable methods, results in an ambiguity, or results in a single best method but that method is not accessible, an error is produced an no further steps are taken.
      • 如果是一个结构类型,则此解决方法允许 ref 传递第一个参数 X ,而 ref 类型为 inThis resolution permits the first argument to be passed by ref if X is a struct type, and the ref kind is in.
    • 如果该方法的返回类型 E GetEnumerator 不是类、结构或接口类型,则会生成错误,并且不会执行任何其他步骤。If the return type E of the GetEnumerator method is not a class, struct or interface type, an error is produced and no further steps are taken.
    • 成员查找是在上 E 用标识符 Current 而不是类型参数执行的。Member lookup is performed on E with the identifier Current and no type arguments. 如果成员查找没有生成任何匹配项,则结果为错误,或结果为除允许读取的公共实例属性之外的任何内容,并不执行任何其他步骤。If the member lookup produces no match, the result is an error, or the result is anything except a public instance property that permits reading, an error is produced and no further steps are taken.
    • 成员查找是在上 E 用标识符 MoveNext 而不是类型参数执行的。Member lookup is performed on E with the identifier MoveNext and no type arguments. 如果成员查找没有生成任何匹配项,则结果为错误,或结果为除方法组外的任何内容,并不执行任何其他步骤。If the member lookup produces no match, the result is an error, or the result is anything except a method group, an error is produced and no further steps are taken.
    • 使用空参数列表对方法组执行重载决策。Overload resolution is performed on the method group with an empty argument list. 如果重载决策导致没有适用的方法,导致不确定性,或者导致单个最佳方法,但该方法是静态的或非公共的,或者它的返回类型不是 bool ,则会生成错误,并且不会执行任何其他步骤。If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, or its return type is not bool, an error is produced and no further steps are taken.
    • 集合类型 _ 是 X , _枚举器类型_ 为 E ,_ 元素类型 是属性的类型 CurrentThe collection type _ is X, the _enumerator type_ is E, and the _ element type is the type of the Current property.
  • 否则,将生成错误,并且不执行任何其他步骤。Otherwise, an error is produced and no further steps are taken.

对于 await foreach ,规则的修改方式也类似。For await foreach, the rules are similarly modified. 该规范所需的唯一更改是 Extension methods do not contribute. 从说明中删除该行,因为该规范的其余部分基于以上规则,这些规则的名称替换为模式方法的不同名称。The only change that is required to that spec is removing the Extension methods do not contribute. line from the description, as the rest of that spec is based on the above rules with different names substituted for the pattern methods.

缺点Drawbacks

每次更改都会增加语言的复杂性,并且这可能会使设计不是 ed 的东西 foreach foreach (如) RangeEvery change adds additional complexity to the language, and this potentially allows things that weren't designed to be foreached to be foreached, like Range.

备选方法Alternatives

不执行任何操作。Doing nothing.

未解决的问题Unresolved questions

目前无。None at this point.