foreach如何调用GetEnumerator()?通过IEnumerable参考还是通过...? [英] How does foreach call GetEnumerator()? Via IEnumerable reference or via...?

查看:64
本文介绍了foreach如何调用GetEnumerator()?通过IEnumerable参考还是通过...?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 静态void Main(string [] args)
{
List< int> listArray = new List< int>();
listArray.Add(100);
foreach(listArray中的int项)
Console.WriteLine(item);
}

a)当 foreach 语句调用 listArray的IEnumerable< int> .GetEnumerator()实现,是否通过 listArray.GetEnumerator()调用它或 IEnumerable< int> .GetEnumerator() IEnumerable.GetEnumerator()



b)同样,当 foreach 引用 listArray的IEnumerable< int> .GetEnumerator(),它是否通过 IEnumerator IEnumerator< int> 引用类型引用此对象?



谢谢



编辑:



我的一些问题将引用此文本:


o在X标识符为GetEnumerator且没有$ b的
类型上执行成员查找$ b类型参数。如果成员查找
不产生匹配,或者
产生歧义,或者产生的非方法组的
匹配,则
将可枚举的接口检查为
如下所述。建议
如果成员
查找产生除
方法组之外的任何内容或不匹配的内容,则发出警告。



o执行使用
重载解析结果的方法组和
空参数列表。如果过载
解析导致没有适用的
方法,导致歧义,或者
导致一个最佳方法,但
该方法是静态的或不是
public ,如下所述检查可枚举的
接口。
建议如果过载解析产生
的东西(明确的公共
实例方法或没有适用的
方法),则发出
警告。



o如果
GetEnumerator方法的返回类型E不是类,
结构或接口类型,则错误为
,并且没有进一步的步骤



o在标识符为Current且没有
类型参数的E
上执行成员查找。如果成员查找
不匹配,则结果为
错误,或者结果为
任何东西,但
允许读取的公共实例属性除外,则产生错误$ b



o在E上执行成员查找,其中
的标识符为MoveNext,并且没有类型的
参数。如果成员查找
不产生匹配项,则结果是
错误,或者结果是任何
,除了方法组外,错误是
且没有进一步的步骤$

o在
方法组上使用空的
参数列表执行重载解析。如果重载解析
没有适用方法,则
会导致歧义,或导致
a最佳最佳方法,但是该方法
是静态的或不公开的,或者它的
的返回类型不是布尔值,将产生
错误,并且不会采取
进一步的操作。



o集合类型为X,则
枚举器类型为E,元素
类型为Current
属性的类型。




  • 否则,请检查可枚举的接口:
    o如果类型T恰好是一个隐式
    从X到接口
    的转换System.Collections.Generic.IEnumerable,
    则收集类型为此
    接口,枚举器类型为
    接口
    System.Collections.Generic.IEnumerator,
    ,元素类型为T。


  • 否则,如果类型T多于一个,则将产生
    错误,并且不执行其他步骤


  • 否则,如果存在从X到X的隐式转换。
    System.Collections.IEnumerable
    接口,则集合类型为
    此接口,枚举器类型为
    接口
    System.Collections.IEnumerator,以及
    的元素类型是object。


  • 否则,将产生错误,并且不再执行
    的步骤。



1)



Eric Lippert的语录:


选项(1)是正确的。请注意,此
意味着返回的枚举数是
一个未装箱的可变结构。



这是一个可变结构
如果您对
进行某些愚蠢的操作,则会带来非常真实的效果,例如传递
的结构,就好像它是
引用类型一样;


来自 http://en.csharp-online.net/ECMA-334:_15.8.4_The_foreach_statement


foreach(V v in x)嵌入语句



然后扩展为:

  {
E e =((C)(x))。GetEnumerator();
try {
V v;
而(e.MoveNext()){
v =(V)(T)e.Current;
嵌入语句
}
}
最后{
…//处理e
}
}

表达式x或
嵌入式语句或任何其他源不可见或无法访问变量e程序的
代码。


如果 listArray ,返回的枚举数保存(即保存其值)在变量 e 中(因此变量 e 是可变结构) 。但是根据上面的摘录, e 不适合我的源代码,所以我将如何传递该结构(除非我编写手动执行的代码 foreach 语句自动执行)?



2)


在标识符为Current且无类型实参的E上执行成员查找。如果成员查找
不匹配,则结果为错误,或者结果为
以外的任何内容,但允许读取的公共实例属性除外,将产生错误,并且不执行
进一步的步骤


似乎如果我们在类中实现 GetEnumerator X 本身,那么 Current 也应在类( E )本身(因此, E 不应显式实现 Current ),因为编译器不会费心检查 IEnumerator< T> / IEnumerator 接口,如果成员查找(在标识符为 Current E 上)不会产生匹配?



3)


如果只有一种类型T表示从X到
接口System.Collections.Generic.IEnumerable进行隐式转换,则集合类型为此
接口,枚举器类型为接口
System.Collections 。Generic.IEnumerator,且元素类型为T。


根据上述内容,如果 foreach 必须检查 IEnumerable< T> 接口,然后 foreach 将始终使用 IEnumerator< T> 版本的当前?因此,如果 E 显式实现 Current IEnumerator< T> 版本c>并且如果它也在类本身中实现了 Current 的另一个版本,则 foreach 将始终调用 IEnumerable< T> 版本的当前



4)


GetEnumerator方法记录为返回以下之一:



http:// msdn.microsoft.com/zh-cn/library/x854yt9s.aspx


您是什么意思其中一个(以复数形式)?您提供的链接说 GetEnumerator (由 List< T> 实现)仅返回 struct 类型。



5)


g。集合类型是X,枚举类型是E,元素类型是Current属性的类型


也许是一个无用的问题-根据上述内容, foreach 不会检查某些用户定义的集合实际存储的元素类型,而是假定元素的类型与返回的类型相同通过当前属性?

解决方案


(a)当foreach语句调用listArray的IEnumerable.GetEnumerator()实现时,它会调用吗通过(1)listArray.GetEnumerator()或(2)IEnumerable.GetEnumerator()或(3)IEnumerable.GetEnumerator()吗?


选项(1)是正确的。 请注意,这意味着返回的枚举数是未装箱的可变结构。 GetEnumerator方法记录为返回以下值之一:



http://msdn.microsoft.com/en-us/library/x854yt9s.aspx



这是事实如果您做一些愚蠢的操作,例如像将其作为引用类型那样传递给该结构,那么可变结构将具有非常实际的效果;


(1)但是根据上面的摘录,我的来源不接受e代码,那么我将如何传递该结构(除非我编写手动执行foreach语句自动执行的代码)?


您是正确的。我不清楚。我的观点是,如果您编写的代码可以执行foreach的功能,并且自己弄乱了枚举器对象,那么您必须要小心。 CLR团队意识到,绝大多数人将使用foreach循环,因此不会遭受不正确使用枚举器的危险。


(2)似乎如果我们在类X本身中实现GetEnumerator,那么Current也应在类E本身中实现,因为在进行成员查找的情况下,编译器不会费心检查显式接口成员不会产生匹配?


正确。


(3)如果foreach必须检查 IEnumerable< T> 接口,则foreach将始终使用 IEnumerator< T> 当前版本?因此,如果E显式实现Current的 IEnumerator< T> 版本,并且如果它也在类本身中实现Current的另一个版本,则foreach将始终调用 IEnumerable< T> 版本的Current?


正确。如果您可以在界面上找到所需的内容,那么我们将使用该界面。


(4)其中之一是什么意思


我的意思是它将返回该结构的实例。


(5)根据上面的内容,foreach不会检查某些用户定义的集合实际存储的元素类型,而是假设元素的类型与Current属性返回的类型相同?


正确。它会检查强制转换是否成功。例如,如果您说

  foreach(int in myObjects)

其中,myObjects为您提供一个枚举器,其枚举类型为Current,然后循环假设每个对象都可以成功地转换为int,并且在运行时抛出异常那是不正确的。但是,如果您说类似的话:

  foreach(字符串in myInts中的值)

然后,编译器会注意到,如果Current返回一个int值,则该集合将永远不会包含字符串,并且将无法编译程序。


(b)同样,当foreach引用listArray的IEnumerable.GetEnumerator()返回的对象时,它是否通过IEnumerator或IEnumerator引用类型引用此对象?


该问题基于对第一个问题的回答(选项2)。由于问题是虚假的,因此无法明智地回答。


    static void Main(string[] args)
    {
        List<int> listArray = new List<int>();
        listArray.Add(100);
        foreach (int item in listArray)
            Console.WriteLine(item);
    }

a) When foreach statement calls listArray's IEnumerable<int>.GetEnumerator() implementation, does it call it via listArray.GetEnumerator() or IEnumerable<int>.GetEnumerator() or IEnumerable.GetEnumerator() ?

b) Similarly, when foreach references object returned by listArray's IEnumerable<int>.GetEnumerator(), does it reference this object via IEnumerator or IEnumerator<int> reference type?

thank you

EDIT:

Some of my questions will quote this text:

o 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.

o 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.

o 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.

o 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.

o 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.

o Overload resolution is performed on the method group with 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, or its return type is not bool, an error is produced and no further steps are taken.

o The 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: o If there is exactly one type T such that there is an implicit conversion from X to the interface System.Collections.Generic.IEnumerable, then the collection type is this interface, the enumerator type is the interface System.Collections.Generic.IEnumerator, and the element type is T.

  • Otherwise, if there is more than one such type T, then an error is produced and no further steps are taken.

  • Otherwise, 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.

  • Otherwise, an error is produced and no further steps are taken.

1)

Quote from Eric Lippert:

Option (1) is correct. Note that this means that the enumerator returned is an unboxed mutable struct.

The fact that this is a mutable struct has very real effects if you do something foolish like passing around the struct as though it were a reference type; it will be copied by value, not by reference.

From http://en.csharp-online.net/ECMA-334:_15.8.4_The_foreach_statement :

foreach (V v in x) embedded-statement

is then expanded to:

{
   E e = ((C)(x)).GetEnumerator();
   try {
      V v;
      while (e.MoveNext()) {
         v = (V)(T)e.Current;
         embedded-statement
      }
   }
   finally {
      … // Dispose e
   }
}

The variable e is not visible to or accessible to the expression x or the embedded statement or any other source code of the program.

In case of listArray, the returned enumerator is saved ( ie its value is saved ) in variable e ( thus variable e is a mutable struct ) .But according to the above excerpt, e is not accesible to my source code, so how would I be able to pass this struct around ( unless I write code that does manually what foreach statement does automatically )?

2)

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.

It seems that if we implement GetEnumerator in the class ( X ) itself, then Current should also be implemented in the class ( E ) itself ( thus E shouldn't explicitly implement Current ), since compiler won't bother to check for IEnumerator<T> / IEnumerator interfaces in cases where member lookup ( on E with identifier Current ) doesn't produce a match?

3)

If there is exactly one type T such that there is an implicit conversion from X to the interface System.Collections.Generic.IEnumerable, then the collection type is this interface, the enumerator type is the interface System.Collections.Generic.IEnumerator, and the element type is T.

According to the above, if foreach has to check for IEnumerable<T> interface, then foreach will always use IEnumerator<T> version of Current? Thus, if E explicitly implements IEnumerator<T> version of Current and if it also implements another version of Current in the class itself, the foreach will always call IEnumerable<T> version of Current?

4)

The GetEnumerator method is documented as returning one of these:

http://msdn.microsoft.com/en-us/library/x854yt9s.aspx

What do you mean by one of these ( as in plural )? Link you provided says GetEnumerator ( as implemented by List<T> ) only returns struct type.

5)

g. The collection type is X, the enumerator type is E, and the element type is the type of the Current property

Perhaps a useless question - according to above, foreach doesn't check what type of elements some user-defined collection actually stores, but instead assumes that the type of elements is the same as the type returned by Current property?

解决方案

(a) When foreach statement calls listArray's IEnumerable.GetEnumerator() implementation, does it call it via (1) listArray.GetEnumerator() or (2) IEnumerable.GetEnumerator() or (3) IEnumerable.GetEnumerator() ?

Option (1) is correct. Note that this means that the enumerator returned is an unboxed mutable struct. The GetEnumerator method is documented as returning one of these:

http://msdn.microsoft.com/en-us/library/x854yt9s.aspx

The fact that this is a mutable struct has very real effects if you do something foolish like passing around the struct as though it were a reference type; it will be copied by value, not by reference.

(1) But according to the above excerpt, e is not accesible to my source code, so how would I be able to pass this struct around ( unless I write code that does manually what foreach statement does automatically )?

You are correct. I was not clear. My point was that if you write code that does what foreach does and you mess with the enumerator object yourself then you have to be careful. The CLR team realized that the vast majority of people would be using the foreach loop and would thereby not be exposed to the hazard of accidentally using the enumerator incorrectly.

(2) It seems that if we implement GetEnumerator in the class X itself, then Current should also be implemented in the class E itself since the compiler won't bother to check for the explicit interface members in cases where member lookup doesn't produce a match?

Correct.

(3) if foreach has to check for IEnumerable<T> interface, then foreach will always use IEnumerator<T> version of Current? Thus, if E explicitly implements IEnumerator<T> version of Current and if it also implements another version of Current in the class itself, the foreach will always call IEnumerable<T> version of Current?

Correct. If you get to the point where we're looking on the interface then we're going to use the interface.

(4) What do you mean by "one of these"

I meant that it will return an instance of the struct.

(5) according to above, foreach doesn't check what type of elements some user-defined collection actually stores, but instead assumes that the type of elements is the same as the type returned by Current property?

Correct. It does check that the cast succeeds. For example, if you say

foreach(int x in myObjects)

where myObjects gives you an enumerator whose Current is of type object, then the loop assumes that each object can be successfully cast to int, and throws an exception at runtime if that is incorrect. But if you say similarly:

foreach(string x in myInts)

then the compiler will note that if Current returns an int then the collection never contains a string, and will fail to compile the program.

(b) Similarly, when foreach references object returned by listArray's IEnumerable.GetEnumerator(), does it reference this object via IEnumerator or IEnumerator reference type?

The question is predicated upon the answer to the first question being option (2). Since the question is predicated upon a falsehood, it cannot be answered sensibly.

这篇关于foreach如何调用GetEnumerator()?通过IEnumerable参考还是通过...?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆