C # 4.0 «динамический» и оператор foreach
Незадолго до того, как я обнаружил, что новыйdynamic
ключевое слово не очень хорошо работает с C #foreach
заявление:
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;
}
}
}
Я ожидал, что итерация поdynamic
Переменная должна работать полностью, как если бы тип переменной коллекции был известен во время компиляции. Я обнаружил, что второй цикл на самом деле выглядит так, когда компилируется:
foreach (object x in (IEnumerable) /* dynamic cast */ (object) new Foo()) {
...
}
и каждый доступ к переменной x приводит к динамическому поиску / приведению, поэтому C # игнорирует, что я указал правильныйx
Тип в выражении foreach - это было немного удивительно для меня ... А также, компилятор C # полностью игнорирует эту коллекцию из динамически типизированной переменной, возможно, реализуетIEnumerable<T>
интерфейс!
Полныйforeach
поведение оператора описано в спецификации C # 4.08.8.4 Утверждение foreach статья.
Но ... Вполне возможно реализовать то же поведение во время выполнения! Можно добавить дополнительныйCSharpBinderFlags.ForEachCast
флаг, исправьте полученный код так:
foreach (int x in (IEnumerable<int>) /* dynamic cast with the CSharpBinderFlags.ForEachCast flag */ (object) new Foo()) {
...
}
И добавить дополнительную логикуCSharpConvertBinder
:
IEnumerable
коллекции иIEnumerator
кIEnumerable<T>
/IEnumerator<T>
.Коллекция Wrap не реализуетIenumerable<T>
/IEnumerator<T>
реализовать эти интерфейсы.Поэтому сегодняforeach
утверждение повторяетсяdynamic
полностью отличается от перебора статически известной переменной коллекции и полностью игнорирует информацию о типе, указанную пользователем. Все это приводит к другому поведению итерации (IEnumarble<T>
Реализация коллекций повторяется как толькоIEnumerable
Реализуется) и более чем150x
замедление при перебореdynamic
, Простое исправление приведет к гораздо лучшей производительности:
foreach (int x in (IEnumerable<int>) dynamicVariable) {
Но почему я должен писать такой код?
Очень приятно видеть, что иногда C # 4.0dynamic
работает точно так же, если тип будет известен во время компиляции, но очень печально видеть, чтоdynamic
работает совершенно по-другому, где IT CAN работает так же, как статически типизированный код.
Итак, мой вопрос: почемуforeach
надdynamic
работает отличается отforeach
над чем-то еще?