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 над чем-то еще?

Ответы на вопрос(1)

Ваш ответ на вопрос