I have a collection of nullable ints.
Why does compiler allows to iteration variable be of type int not int? ?
List<int?> nullableInts = new List<int?>{1,2,3,null};
List<int> normalInts = new List<int>();
//Runtime exception when encounter null value
//Why not compilation exception?
foreach (int i in nullableInts)
{
//do sth
}
Of course I should pay attention to what I iterate through but it would be nice if compiler reprimanded me:) Like here:
foreach (bool i in collection)
{
// do sth
}
//Error 1 Cannot convert type 'int' to 'bool'
Update
OK, originally I said “the compiler adds casts to a
foreachloop.” This isn’t strictly accurate: it won’t always add casts. Here’s what’s really happening.First of all, when you have this
foreachloop:…here is the basic outline (in pseudo-C#) of what the compiler creates:
What? I hear you saying. What do you mean by
[object]?”Here’s what I mean. The
foreachloop actually requires no interface, which means it’s actually a little bit magical. It only requires that the type of object being enumerated exposes aGetEnumeratormethod, which in turn must provide an instance of some type that provides aMoveNextand aCurrentproperty.So I wrote
[object]because the type ofedoes not necessarily have to be an implementation ofIEnumerator<int>, or evenIEnumerator— which also means it doesn’t necessarily have to implementIDisposable(hence the[dispose if necessary]part).The part of the code we care about for the purpose of answering this question is the part where I wrote
[cast if possible]. Clearly, since the compiler doesn’t require an actualIEnumerator<T>orIEnumeratorimplementation, the type ofe.Currentcannot be assumed to beT,objector anything in between. Instead, the compiler determines the type ofe.Currentbased on the type returned byGetEnumeratorat compile time. Then the following happens:xin the above example), a straight assignment is used.e.Currentto the type ofx), a cast is inserted.So in the scenario of enumerating over a
List<int?>, we get to step 2 and the compiler sees that theList<int?>.Enumeratortype’sCurrentproperty is of typeint?, which can be explicitly cast toint.So the line can be compiled to the equivalent of this:
Now, what does the
explicitoperator look like forNullable<int>?According to Reflector:
So the behavior described by Kent is, as far as I can tell, simply a compiler optimization: the
(int)e.Currentexplicit cast is inlined.As a general answer to your question, I stick by my assertion that the compiler inserts casts in a
foreachloop where needed.Original Answer
The compiler automatically inserts casts where needed in a
foreachloop for the simple reason that before generics there was noIEnumerable<T>interface, onlyIEnumerable*. TheIEnumerableinterface exposes anIEnumerator, which in turn provides access to aCurrentproperty of typeobject.So unless the compiler performed the cast for you, in olden times, the only way you could’ve used
foreachwould’ve been with a local variable of typeobject, which obviously would’ve sucked.*And actually,
foreachdoesn’t require any interface at all — only theGetEnumeratormethod and an accompanying type with aMoveNextand aCurrent.