Реинициализация в рамках "использования" блока является плохой идеей, чтобы избежаться в любом случае. Тем не менее я собираюсь спросить это:
Почему "использование" вызова располагает на исходном значении а не на последней ссылке или реинициализации (который происходит, если попытка наконец блок используется),
MyClass b = new MyClass();// implements Idisposable
MyClass c = new MyClass();
MyClass a ;
using (a = new MyClass())
{
a = b;
a = c;
}
В вышеупомянутом коде располагают, будет назван на исходной ссылке а не более новом, на которое ссылаются. Это может быть легко проверено путем печати чего-то на консоли в расположить методе.
Однако с попыткой {} наконец кодируют последнюю ссылку, располагают метод, назван.
try
{
a = new MyClass();
a = b;
a = c;
}
finally
{
a.Dispose();
}
MSDN: оператор использования гарантирует, чтобы Расположили, назван, даже если исключение происходит, в то время как Вы - вызывающие методы для объекта.
using (Font font1 = new Font("Arial", 10.0f))
{
byte charset = font1.GdiCharSet;
}
В основном "использование" переводит в:
{
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}
Компилятор генерирует следующий код:
MyClass b = new MyClass();
MyClass a;
MyClass cs$3$000 = a = new MyClass();
try {
a = b;
}
finally {
if (cs$3$000 != null) cs$3$000.Dispose();
}
Автоматически сгенерированная локальная переменная cs $ 3 $ 000 реализует контракт.
Есть две формы с использованием операторов
, определенных в спецификации C #:
using-statement:
using ( resource-acquisition ) embedded-statement
resource-acquisition:
local-variable-declaration
expression
Если у вас есть объявление-локальной-переменной
, вопросов не возникнет. Переменная будет доступна только для чтения в блоке using, и вы вообще не сможете ее изменить. В спецификации говорится:
8.13 Оператор using
[...] В любом раскрытии переменная ресурса доступна только для чтения в встроенный оператор.
Здесь мы имеем дело со второй формой: где получение ресурса
- это выражение
, а не объявление локальной переменной
. В этом случае спецификация C # ясно говорит:
Оператор using формы
оператор using (выражение)
имеет те же два возможных расширения, но в этом случае ResourceType неявно является тип выражения во время компиляции, и переменная ресурса недоступна и невидима для встроенного оператора . [курсив мой]
Очевидно, вы не можете изменить невидимую, недоступную переменную. Его значение присваивается только в предложении using resource-capture
. Следовательно, у него будет старое значение переменной, а не новое.
Когда вы имеете дело с присваиванием уже объявленной переменной , вы используете эту форму оператора using
. Тот факт, что вы присваиваете значение переменной , например
using ( x = something )
, не имеет значения. Целое x = something
рассматривается как выражение, и только значение этого выражения имеет значение.Важно знать, что «переменная ресурса» здесь не «x». Это невидимая переменная. С точки зрения компилятора, нет большой разницы между следующими конструкциями:
using ( something )
using ( x = something )
using ( y = x = something )
Во всех случаях будет выполняться выражение и значение - это то, что получит гарантированное удаление, а не переменная. Что должен был бы сделать компилятор, если бы это не было определенным поведением и вы написали третью строку в приведенном выше блоке? Утилизировать x
? и
? оба? ни один? Текущее поведение имеет смысл.
Using можно рассматривать как обещание вызвать disposed на объекте, объявленном с помощью using. Это единственное, что, ИМХО, имеет смысл!
Если вы вызовете dispose на переназначенном значении, то исходное значение не будет утилизировано.
Dispose будет вызван на объекте, на который ссылается аргумент в выражении using. Все просто.
Похоже, что "using" создает свою собственную переменную для хранения ссылки, которая инициализируется на "a", который в свою очередь инициализируется на первый экземпляр объекта. Позже, когда вы изменяете "a", вы на самом деле не изменяете исходную ссылку, которая была сохранена в операторе "using". Это кажется довольно хорошей функцией, поскольку using отвечает за утилизацию фактического объекта, упомянутого в операторе using, а не переменной.
Да, это интересно.
Итак, я посмотрел на декомпилированный код:
IL_0000: nop
IL_0001: newobj instance void ConsoleApplication17.Foo1::.ctor()
IL_0006: dup
IL_0007: stloc.0
IL_0008: stloc.1 // 1. note this
.try
{
IL_0009: nop
IL_000a: newobj instance void ConsoleApplication17.Foo2::.ctor()
IL_000f: stloc.0 // 2. and this
IL_0010: nop
IL_0011: leave.s IL_0023
} // end .try
finally
{
IL_0013: ldloc.1 // 3. and this
IL_0014: ldnull
IL_0015: ceq
IL_0017: stloc.2
IL_0018: ldloc.2
IL_0019: brtrue.s IL_0022
IL_001b: ldloc.1
IL_001c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0021: nop
IL_0022: endfinally
} // end handler
IL_0023: nop
Итак, он действительно копирует ссылку и использует ее копию позже, позволяя вам сбросить переменную, не достигая ничего, с точки зрения финализатора.
Действительно, было бы неплохо, если бы в операторе using можно было использовать только переменные "только для чтения". Это немного сбивает с толку. И, конечно, MSDN вводит в заблуждение.