Обновление: теперь я пошел и сделал это: I отправил отчет об ошибке в Microsoft, так как я серьезно сомневаюсь, что это правильное поведение. Тем не менее, я все еще не уверен на 100%, чему верить относительно этого вопроса; так что я вижу, что то, что «правильно», открыто для некоторого уровня интерпретации.
Мне кажется, что либо Microsoft согласится с тем, что это ошибка, либо ответит, что изменение переменной типа изменяемого значения в операторе using
представляет собой неопределенное поведение.
Кроме того, как бы то ни было, у меня есть, по крайней мере, предположение о том, что здесь происходит. Я подозреваю, что компилятор создает класс для закрытия, «поднимая» локальную переменную до поля экземпляра этого класса; и поскольку он находится внутри блока using
, он создает поле readonly
. Как указал Люк Х в комментарий к другому вопросу, это помешает вызовам методов, таких как 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
+). Мне непонятно, правильно ли это поведение или нет.
Дополнительные мои мысли по этому поводу см. На странице здесь.
foreach
обычно дает неожиданные, если они правильные, результаты. - person Gabe   schedule 10.01.2011foreach
. - person LukeH   schedule 10.01.2011using
plus. Мне кажется, он должен вести себя либо в одном направлении, при наличии замыкания, либо последовательно; или он должен постоянно вести себя наоборот. В нынешнем виде меня беспокоит непоследовательность. Тем не менее, я готов верить, что может быть объяснение, которое для меня просто неочевидно ... - person Dan Tao   schedule 10.01.2011readonly
, но, что интересно, IL, который обращается к полю в блокеusing
, точно такой же, как если бы поле былоreadonly
(т. е. скопируйте поле в локальное и измените локальное вместо того, чтобы изменять само поле). - person LukeH   schedule 13.01.2011