Reputation: 61
C# spec says that foreach statement will be expanded to:
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current; // <-- double cast. For what?
embedded-statement
}
}
finally {
… // Dispose e
}
}
Why the following will be incorrect?
V v = (V)e.Current;
Upvotes: 3
Views: 95
Reputation: 70523
To help answer the question lets get some contenxt, the prior lines of the specification say:
The above steps, if successful, unambiguously produce a collection type C, enumerator type E and element type T. A foreach statement of the form
foreach (V v in x) embedded-statement
is then expanded to
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current; // <-- double cast. For what?
embedded-statement
}
}
finally {
… // Dispose e
}
}
Prior to this is a whole section about how to determine what 'C', 'E', and 'T' are.
I suggest anyone who is interested read it, BUT what is important the type of V can be different than than the type returned by the enumeration on x which has to be able to cast to type T (but can still be a different type) and T has to be able to cast to V.
So this:
V v = (V)(T)e.Current;
Does that... first it take the enumerations current value and casts it to the type T and then casts that to the type V.
For example consider the following code
void Main()
{
double [] values = { 1.1,2.2,3.3,4.4,5.5 };
foreach(int number in values)
{
Console.WriteLine(number);
}
}
In this case T is double and V is int.
Upvotes: 2
Reputation:
A simple example where the double cast is necessary is an array:
short[] array = { 1, 2 };
foreach (int i in array) {
// ...
}
The element type is short
, because the type of the expression is array of short
. However, although arrays do implement IEnumerable<T>
, the collection type for purposes of foreach
is plain old IEnumerable
, whose GetEnumerator()
method returns a plain old IEnumerator
, and its Current
property has type object
. A cast directly to int
would produce an InvalidCastException
.
It would have been possible to specify that for an array of short
, the collection type is IEnumerable<short>
, but that would break for multidimensional arrays, because those only implement the non-generic IEnumerable
.
Upvotes: 1
Reputation: 149538
I recommend reading eric lipperts excellent blog post:
http://ericlippert.com/2013/07/22/why-does-a-foreach-loop-silently-insert-an-explicit-conversion/
Ill quote him, so points might as well go to him because this is all him:
The answer is: the foreach loop semantics were designed before generics were added to the language; a highly likely scenario is that the collection being enumerated is an ArrayList or other collection where the element type is unknown to the compiler, but is known to the developer. It is rare for an ArrayList to contain ints and strings and Exceptions and Customers; usually an ArrayList contains elements of uniform type known to the developer. In a world without generics you typically have to know that ahead of time by some means other than the type system telling you. So just as a cast from object to string is a hint to the compiler that the value is really a string, so too is
Upvotes: 1