Обновить: Ну, теперь я пошел и сделал это: я подал отчет об ошибке в Microsoft об этом, поскольку я серьезно сомневаюсь, что это правильное поведение. Тем не менее, я все еще не уверен на 100%, что поверить в этот вопрос; поэтому я вижу, что то, что является "правильным", открыто для некоторого уровня интерпретации.
Я чувствую, что либо Microsoft признает, что это ошибка, либо отвечает, что изменение переменной изменяемого типа значения в операторе using
представляет собой поведение undefined.
Кроме того, для чего это стоит, у меня есть, по крайней мере, догадка о том, что здесь происходит. Я подозреваю, что компилятор генерирует класс для закрытия, "поднимая" локальную переменную в поле экземпляра этого класса; и поскольку он находится в блоке 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
+). Является ли это поведение правильным или нет, неясно мне.
Еще несколько моих мыслей по этому вопросу см. здесь .