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