Обновление : Хорошо, теперь я пошел и сделал это: я подал в Microsoft отчет об ошибке об этом, так как я серьезно сомневаюсь, что это правильное поведение. Тем не менее, я все еще не уверен на 100%, во что верить в отношении этого вопроса ; так что я могу видеть, что то, что является «правильным», открыто для некоторого уровня интерпретации.
Мне кажется, что либо Microsoft примет это за ошибку, либо ответит, что изменение переменной типа изменяемого значения в операторе using
представляет собой неопределенное поведение.
Кроме того, для чего бы это ни стоило, у меня есть по крайней мере догадка относительно того, что здесь происходит. Я подозреваю, что компилятор генерирует класс для замыкания, «поднимая» локальную переменную в поле экземпляра этого класса; и поскольку он находится внутри блока using
, создает поле readonly
. Как указывал LukeH в комментарии к другому вопросу , это помешало бы вызовам методов, таким как MoveNext
, изменять само поле (вместо этого они влияли бы на копию).
Примечание: я сократил этот вопрос для удобства чтения, хотя он еще не совсем короткий. Полный текст оригинального (более длинного) вопроса см. В истории изменений.
Я прочитал, как мне кажется, соответствующие разделы ECMA-334 и не могу найти окончательного ответа на этот вопрос. Сначала я сформулирую вопрос, а затем предоставлю ссылку на некоторые дополнительные комментарии для тех, кто заинтересован.
Вопрос
Если у меня есть изменяемый тип значения, который реализует IDisposable
, я могу (1) вызвать метод, который изменяет состояние значения локальной переменной в операторе using
, и код ведет себя так, как я ожидаю. Однако, как только я фиксирую рассматриваемую переменную в закрытии внутри в операторе using
, (2) изменения значения больше не видны в локальной области действия.
Такое поведение проявляется только в случае, когда переменная захвачена внутри замыкания и в операторе using
; не очевидно, когда присутствует только одно (using
) или другое условие (замыкание).
Почему захват переменной типа изменяемого значения внутри замыкания в операторе using
меняет ее локальное поведение?
Ниже приведены примеры кода, иллюстрирующие элементы 1 и 2. В обоих примерах будет использоваться следующая демонстрация Mutable
тип значения:
struct Mutable : IDisposable
{
int _value;
public int Increment()
{
return _value++;
}
public void Dispose() { }
}
1. Преобразование переменной типа значения в блоке using
using (var x = new Mutable())
{
Console.WriteLine(x.Increment());
Console.WriteLine(x.Increment());
}
Выходной код выводит:
0
1
2. Захват переменной типа значения внутри замыкания в блоке using
using (var x = new Mutable())
{
// x is captured inside a closure.
Func<int> closure = () => x.Increment();
// Now the Increment method does not appear to affect the value
// of local variable x.
Console.WriteLine(x.Increment());
Console.WriteLine(x.Increment());
}
Вышеприведенный код выводит:
0
0
Дополнительные комментарии
Было отмечено, что компилятор Mono обеспечивает ожидаемое мной поведение (изменения значения локальной переменной все еще видны в случае закрытия using
+). Правильно ли это поведение или нет, мне неясно.
Подробнее о моих мыслях по этому вопросу см. здесь .