在“使用”块中重新初始化是一个坏主意,要始终避免。我仍然会问这个问题:
为什么“使用”调用处理原始值而不是最后一次引用或重新初始化(如果使用try finally block则会发生这种情况)
MyClass b = new MyClass();// implements Idisposable
MyClass c = new MyClass();
MyClass a ;
using (a = new MyClass())
{
a = b;
a = c;
}
在上面的代码中,dispose将在原始引用上调用,而不是引用的较新引用。这可以通过在dispose方法中在控制台上打印来轻松验证。
然而,使用try {} finally代码调用最后一个引用dispose方法。
try
{
a = new MyClass();
a = b;
a = c;
}
finally
{
a.Dispose();
}
MSDN:using语句确保即使在对象上调用方法时发生异常也会调用Dispose。
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();
}
}
答案 0 :(得分:8)
C#规范中定义了两种形式的using
语句:
using-statement:
using ( resource-acquisition ) embedded-statement
resource-acquisition:
local-variable-declaration
expression
如果您有local-variable-declaration
,则不会有任何问题。该变量在使用块中是只读的,您根本无法更改它。规范说:
8.13使用声明
[...] 在任一扩展中,资源变量在嵌入语句中都是只读的。
在这里,我们正在处理第二种形式:resource-acquisition
是expression
而不是local-variable-declaration
。在这种情况下,C#规范清楚地说:
表格的使用声明
using (expression) statement
具有相同的两个可能的扩展,但在这种情况下,ResourceType隐式地是表达式的编译时类型,并且资源变量在嵌入语句中是不可访问的,并且是不可访问的。 [强调我的]
显然,您无法更改不可见的,无法访问的变量。其值仅在using resource-acquisition
子句中指定。因此,它将具有变量的旧值,而不是新值。
当您处理已声明的变量的赋值时,您正在使用using
语句的这种形式。您为变量分配值
using ( x = something )
无关紧要。整个x = something
被视为表达式,只有该表达式的值才是重要的。重要的是要知道“资源变量”在这里不是“x”。这是一个看不见的变量。从编译器的角度来看,以下结构之间没有太大区别:
using ( something )
using ( x = something )
using ( y = x = something )
在所有情况下,表达式都将被执行,值将得到保证处理,而不是变量。如果这不是定义的行为并且您在上面的块中编写了第三行,编译器应该做什么?处理x
? y
?都?都不是?目前的行为是有道理的。
答案 1 :(得分:3)
使用可以看作是对使用声明声明的对象进行调用的承诺。恕我直言,这是唯一有意义的事情!
如果您对重新分配的值调用dispose,则不会处理原始值。
答案 2 :(得分:3)
编译器生成此代码:
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局部变量实现了契约。
答案 3 :(得分:1)
似乎“using”正在创建它自己的变量来存储引用,它被初始化为“a”,而“a”又被初始化为对象的第一个实例。稍后当您更改“a”时,您并未真正更改存储在“using”语句中的原始引用。这似乎是一个非常好的功能,因为using负责处理using语句中提到的实际对象,而不是变量。
答案 4 :(得分:1)
是的,这很有意思。
所以我查看了反编译的代码:
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会产生误导。
答案 5 :(得分:0)
将在using子句的参数中引用的对象上调用Dispose。就这么简单。