I ran into the following code:
foreach (var row in datatable.Rows.Cast<DataRow>())
{
some code here
}
Now, I think casting in a foreach loop is incorrect, since I think it cast each time it loops. But, I could be wrong, maybe the compiler is smart enough?
On a related note: How can I figure something like this out for myself?
Well it calls Cast<DataRow> once, but that will actually cast each item as it's fetched. So there is a cast on each iteration (at least potentially; Cast has an optimization when it knows it's not necessary) but there's only a single method call. The iterator returned by Cast will fetch items (lazily) from the source (datatable.Rows here) and cast each item as it's requested by the loop.
See my Edulinq post on Cast for more information.
Although the waters are a bit muddied here due to Cast being lazy, it's important to remember that the expression to the right of in is only ever evaluated once. So a loop of the form:
foreach (var item in complicatedExpression)
{
...
}
is equivalent to:
var items = complicatedExpression;
foreach (var item in items)
{
...
}
The compiler will generate code that calls GetEnumerator() once, and then uses the result of that to iterate over the collection.
For this specific example, an alternative to using Cast would be to let the compiler put the cast in the loop itself:
foreach (DataRow row in datatable.Rows)
{
some code here
}
The compiler will perform a cast on each item as it fetches it from the iterator, implicitly. It's a bit sneaky because it's not obviously casting.
As for how you can tell what's going on - you could always use ildasm to look at the IL. That can be very instructive, but somewhat time-consuming.
The IEnumerable<T>.Cast<TResult> extension method returns an enumerable thats lazily evaluated. This means that every time you request the 'next' item from it, it retrieves it from the underlying datasource (the datatable in your case), and casts it to the specified type. So casting in a foreach is not incorrect, since the casting is not done 'too much', it is done for every item, when it is necessary. Adding a .ToList() is not better, since this garantuees to cast every item, even when you not need to, for example, if you exit the loop with a break.
You can find this out yourself by reading the documentation, and reading articles about the method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With