5

在“使用”块内重新初始化是一个坏主意,应始终避免。我还是要问这个:

为什么“使用”调用处理原始值而不是最后一个引用或重新初始化(如果使用 try finally 块会发生这种情况)

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();
  }
}
4

6 回答 6

8

usingC# 规范中定义了两种形式的语句:

using-statement:
    using   (    resource-acquisition   )    embedded-statement
resource-acquisition:
    local-variable-declaration
    expression

如果你有一个local-variable-declaration,就不会有任何问题。该变量在 using 块中将是只读的,您根本无法更改它。规范说:

8.13 using 语句

[...]在任一扩展中,资源变量在嵌入语句中都是只读的。

在这里,我们处理的是第二种形式: where resource-acquisitionisexpression和 not a local-variable-declaration。在这种情况下,C# 规范清楚地说:

形式的使用语句

 using (expression) statement

具有相同的两种可能的扩展,但在这种情况下,ResourceType 隐含地是表达式的编译时类型,并且资源变量在嵌入的语句中是不可访问的,也不可见的[强调我的]

显然,您无法更改不可见、不可访问的变量。它的值仅在 usingresource-acquisition子句中分配。因此,它将具有变量的旧值,而不是新值。

当您处理对已声明变量的赋值时,您正在使用这种形式的using语句。您正在为变量赋值的事实

using ( x = something )

无关紧要。整体x = something被视为一个表达式,只有该表达式的值才是最重要的。重要的是要知道这里的“资源变量”不是“x”。这是一个看不见的变量。从编译器的角度来看,以下构造之间没有太大区别:

using ( something ) 
using ( x = something )
using ( y = x = something )

在所有情况下,表达式都将被执行,并且将得到保证处理,而不是变量。如果这不是定义的行为并且您在上面的块中编写了第三行,编译器应该怎么做?处置xy? 两个都?两者都不?当前的行为是有道理的。

于 2010-02-22T13:00:14.583 回答
3

using 可以看作是对使用 using 声明的对象调用的承诺。恕我直言,这是唯一有意义的事情!

如果您对重新分配的值调用 dispose,则不会释放原始值。

于 2010-02-22T12:57:27.070 回答
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 局部变量实现了合约。

于 2010-02-22T13:08:33.440 回答
1

似乎“使用”正在创建它自己的变量来存储引用,该变量被初始化为“a”,而“a”又被初始化为对象的第一个实例。稍后当您更改“a”时,您并没有真正更改存储在“using”语句中的原始引用。这似乎是一个非常好的功能,因为 using 负责处理 using 语句中提到的实际对象,而不是变量。

于 2010-02-22T12:58:52.650 回答
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 具有误导性。

于 2010-02-22T13:05:13.840 回答
0

Dispose 将在 using 子句的参数中引用的对象上调用。就是这么简单。

于 2010-02-22T12:57:34.617 回答