rzaitov
rzaitov

Reputation: 61

What difference between the element type and the loop variable type inside foreach loop?

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

Answers (3)

Hogan
Hogan

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

user743382
user743382

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

Yuval Itzchakov
Yuval Itzchakov

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

Related Questions