C# 4.0 'dynamic' and foreach statement
Posted
by ControlFlow
on Stack Overflow
See other posts from Stack Overflow
or by ControlFlow
Published on 2010-05-30T14:33:56Z
Indexed on
2010/05/30
14:42 UTC
Read the original article
Hit count: 617
Not long time before I've discovered, that new dynamic
keyword doesn't work well with the C#'s foreach
statement:
using System;
sealed class Foo {
public struct FooEnumerator {
int value;
public bool MoveNext() { return true; }
public int Current { get { return value++; } }
}
public FooEnumerator GetEnumerator() {
return new FooEnumerator();
}
static void Main() {
foreach (int x in new Foo()) {
Console.WriteLine(x);
if (x >= 100) break;
}
foreach (int x in (dynamic)new Foo()) { // :)
Console.WriteLine(x);
if (x >= 100) break;
}
}
}
I've expected that iterating over the dynamic
variable should work completely as if the type of collection variable is known at compile time. I've discovered that the second loop actually is looked like this when is compiled:
foreach (object x in (IEnumerable) /* dynamic cast */ (object) new Foo()) {
...
}
and every access to the x variable results with the dynamic lookup/cast so C# ignores that I've specify the correct x
's type in the foreach statement - that was a bit surprising for me... And also, C# compiler completely ignores that collection from dynamically typed variable may implements IEnumerable<T>
interface!
The full foreach
statement behavior is described in the C# 4.0 specification 8.8.4 The foreach statement article.
But... It's perfectly possible to implement the same behavior at runtime! It's possible to add an extra CSharpBinderFlags.ForEachCast
flag, correct the emmited code to looks like:
foreach (int x in (IEnumerable<int>) /* dynamic cast with the CSharpBinderFlags.ForEachCast flag */ (object) new Foo()) {
...
}
And add some extra logic to CSharpConvertBinder
:
- Wrap
IEnumerable
collections andIEnumerator
's toIEnumerable<T>
/IEnumerator<T>
. - Wrap collections doesn't implementing
Ienumerable<T>
/IEnumerator<T>
to implement this interfaces.
So today foreach
statement iterates over dynamic
completely different from iterating over statically known collection variable and completely ignores the type information, specified by user. All that results with the different iteration behavior (IEnumarble<T>
-implementing collections is being iterated as only IEnumerable
-implementing) and more than 150x
slowdown when iterating over dynamic
. Simple fix will results a much better performance:
foreach (int x in (IEnumerable<int>) dynamicVariable) {
But why I should write code like this?
It's very nicely to see that sometimes C# 4.0 dynamic
works completely the same if the type will be known at compile-time, but it's very sadly to see that dynamic
works completely different where IT CAN works the same as statically typed code.
So my question is: why foreach
over dynamic
works different from foreach
over anything else?
© Stack Overflow or respective owner