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?
See Question&Answers more detail:
os