Почему не откладывать выполнение кеша итеративных значений? - PullRequest
4 голосов
/ 09 июня 2009

Возьмите код ниже, адаптированный из этот вопрос :

//Borrowed from another question because its a simpler example of what happened to me.
IEnumerable<char> query = "Not what you might expect";
foreach(char vowel in "aeiou")
{
     query = query.Where(c => c != vowel);
}
foreach (char Output in query)
{
    System.Out.WriteLine(Output);
}

Это только удаляет 'u' из коллекции символов запроса. Основная проблема связана с тем, что переменная c в предложении Where не оценивается до второго foreach. Мой вопрос:

1) Почему делегат, сгенерированный первым foreach, не захватил каждое значение c, когда оно построено? Есть ли какая-то ситуация, о которой я не знаю, где это нежелательное поведение?

2) Если оно не захватывает значение c, как это значение остается в области действия во втором элементе foreach, когда запрос фактически выполняется? Мне может показаться, что если не хранить значения передаваемых переменных, то попытка разрешить оператор для второго foreach потерпит неудачу, поскольку переменная c явно находится вне области видимости.

Я не понимаю, почему «использовать последнее значение, которое мы видели для этой переменной назад, когда она находилась в области видимости», было хорошим конструктивным решением для этого обстоятельства, и я надеялся, что кто-то сможет пролить свет на эту тему.

Ответы [ 3 ]

7 голосов
/ 09 июня 2009

Захват vowel; попробуйте:

foreach(char vowel in "aeiou") {
     char tmp = vowel;
     query = query.Where(c => c != tmp);
}

У Джона есть список похожих сообщений здесь .

Что касается того, почему ... foreach определяется (в ECMA 334v4 §15.8.4) как:

A foreach statement of the form `foreach (V v in x) embedded-statement` is then expanded to:

{
  E e = ((C)(x)).GetEnumerator();
  try {
    V v;
    while (e.MoveNext()) {
      v = (V)(T)e.Current;
      embedded-statement
    }
  }
  finally {
    … // Dispose e
  }
}

Обратите внимание, что V это за пределами * while - это означает, что он захватывается только один раз для foreach Я понимаю, что команда C # порой сомневалась в целесообразности этого изменения (это было внутри while в спецификации C # 1.2, что позволило бы полностью избежать этой проблемы). Возможно, со временем все изменится, но сейчас это соответствует спецификации.

4 голосов
/ 09 июня 2009

Как говорит Марк, он фиксирует vowel как переменную, а не значение vowel в каждом случае. Это почти всегда нежелательно, но является указанным поведением (т. Е. Компилятор следует спецификации языка). Для каждой итерации существует одна переменная гласного, а не «новый экземпляр переменной».

Обратите внимание, что здесь не используется дерево выражений - вы используете IEnumerable<T>, поэтому он просто конвертирует лямбда-выражение в делегат.

См. Раздел 7.14.4 спецификации C # 3 для получения дополнительной информации.

1 голос
/ 09 июня 2009

Я думаю, что вы действительно хотите сделать здесь что-то вроде этого:

IEnumerable<char> query = "Not what you might expect";
query = query.Except("aeiou");

foreach (char Output in query)
{
    System.Out.WriteLine(Output);
}
...