Update
OK, initially I said: "the compiler adds castings to the foreach ." This is not entirely accurate: he will not always add castings. This is what really happens.
First of all, when you have this foreach :
foreach (int x in collection) { }
... here is the main outline (in pseudo-C #) of what the compiler creates:
int x; [object] e; try { e = collection.GetEnumerator(); while (e.MoveNext()) { x = [cast if possible]e.Current; } } finally { [dispose of e if necessary] }
What? I can hear you talking. What do you mean by [object] ? "
Here is what I mean. The foreach does not actually require an interface, which means that it is actually a bit magical. It only requires that the type of the object to be enumerated be provided by the GetEnumerator method, which in turn must provide an instance of some type that provides the MoveNext and Current properties.
So, I wrote [object] because the type e does not have to be an implementation of IEnumerator<int> or even IEnumerator - which also means that it is not necessary to implement IDisposable (hence the [dispose if necessary] ).
The part of the code that we care about to answer this question is the part in which I wrote [cast if possible] . It is clear that since the compiler does not require the actual implementation of IEnumerator<T> or IEnumerator , the e.Current type e.Current not be considered T , object or anything in between. Instead, the compiler determines the e.Current type based on the type returned by GetEnumerator at compile time. Then the following happens:
- If the type is a local variable type (
x in the above example), direct assignment is used. - If the type is converted to the type of a local variable (by which I mean that a legitimate listing exists from
e.Current to type x ), the listing is inserted. - Otherwise, the compiler will throw an error.
So, in the List<int?>.Enumerator script, we go to step 2, and the compiler sees that the List<int?>.Enumerator Property List<int?>.Enumerator type Current is of type int? which can be explicitly passed to int .
Thus, the string can be compiled in the equivalent:
x = (int)e.Current;
Now, what does the explicit statement for a Nullable<int> look like?
According to reflector:
public static explicit operator T(T? value) { return value.Value; }
So, the behavior described by Kent , as far as I can tell, is just compiler optimization: an explicit listing of (int)e.Current is built-in.
As a general answer to your question, I stick to my assertion that the compiler inserts into foreach loops if necessary.
Original answer
The compiler automatically adds casts if necessary in the foreach for the simple reason that before generics there was no IEnumerable<T> interface, only IEnumerable *. The IEnumerable interface provides an IEnumerator , which in turn provides access to the Current property of type object .
So, if the compiler didn’t do the throw for you, in the old days, the only way you could use foreach would be with a local variable of type object , which obviously would suck.
* And in fact, foreach does not require any interface at all - only the GetEnumerator method and associated type with MoveNext and a Current .