C#:使用块:对象重新初始化

发布于 2024-08-22 06:24:40 字数 1044 浏览 8 评论 0原文

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

为什么“using”调用 dispose 是在原始值上,而不是在最后一个引用或重新初始化上(如果使用 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();
  }
}

Re-initialization within "using" block is a bad idea, to be avoided at all times. Still i am going to ask this:

Why does "using" call dispose on the original value and not on the last reference or re-initialization (which happens if try finally block is used)

MyClass b = new MyClass();// implements Idisposable
MyClass c = new MyClass();
MyClass a ; 

 using (a = new MyClass())
 {
                a = b;
                a = c;
 }

In the above code dispose will be called on original reference and not the newer referenced. This can be easily verified by printing something on console in the dispose method.

However with try{} finally code the last reference dispose method is called.

try
{
   a = new MyClass();
   a = b;
   a = c;
 }
  finally 
   {
   a.Dispose();
  }

MSDN : The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object.

using (Font font1 = new Font("Arial", 10.0f)) 
{
    byte charset = font1.GdiCharSet;
}

Basically "using" translates to:

{
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

时光礼记 2024-08-29 06:24:40

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

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

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

8.13 using 语句

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

在这里,我们处理第二种形式:其中资源获取表达式,而不是本地变量声明。在这种情况下,C# 规范明确指出:

格式为

的 using 语句

 using (表达式) 语句

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

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

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

using ( x = something )

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

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

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

There are two forms of using statements defined in the C# specification:

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

If you have a local-variable-declaration, there wouldn't be any questions. The variable will be read-only in the using block and you can't change it at all. The spec says:

8.13 The using statement

[...] In either expansion, the resource variable is read-only in the embedded statement.

Here, we're dealing with the second form: where resource-acquisition is expression and not a local-variable-declaration. In which case, the C# spec clearly says:

A using statement of the form

 using (expression) statement

has the same two possible expansions, but in this case ResourceType is implicitly the compile-time type of the expression, and the resource variable is inaccessible in, and invisible to, the embedded statement. [emphasis mine]

Obviously, you can't change an invisible, inaccessible variable. Its value is assigned only in the using resource-acquisition clause. Therefore, it'll have the old value of the variable, not the new one.

When you are dealing with an assignment to an already declared variable, you are using this form of the using statement. The fact that you are assigning a value to a variable like

using ( x = something )

is irrelevant. The whole x = something is treated as an expression and only the value of that expression is what matters. It's important to know that "resource variable" is not "x" here. It's an invisible variable. From the compiler's perspective, there's not much difference between the following constructs:

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

In all cases, the expression will get executed and the value is what will get guaranteed disposal, not the variable. What would the compiler supposed to do if this wasn't the defined behavior and you had written the third line in the above block? Dispose x? y? both? neither? The current behavior makes sense.

☆獨立☆ 2024-08-29 06:24:40

编译器生成以下代码:

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 局部变量实现合约。

The compiler generates this code:

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

The auto-generated cs$3$000 local variable implements the contract.

夏日浅笑〃 2024-08-29 06:24:40

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

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

The using can be seen as a promise to call disposed on the object declared with using. This is the only thing that, IMHO, makes sense!

If you would call dispose on the reassigned value, then the original value will not be disposed.

北城半夏 2024-08-29 06:24:40

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

It seems that the "using" is creating it's own variable to store the reference, that gets initialized to "a" which in turn is initialized to the first instance of the object. Later when you change "a", you're not really changing the original reference that was stored in the "using" statement. It seems like a pretty good feature, since the using is responsible for disposing the actual object mentioned in the using statement, not the variable.

擦肩而过的背影 2024-08-29 06:24:40

是的,这很有趣。

所以我查看了反编译的代码:

  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 具有误导性。

Yeah, it's interesting.

So I looked at the decompiled code:

  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

So it really copies the reference, and uses it's copy later, allowing you to reset the variable, without really achieving anything, in terms of the finaliser.

Really, it would be nice if you could only use 'read-only' variables in the using statement. It's a little confusing. And certainly, the MSDN is misleading.

不醒的梦 2024-08-29 06:24:40

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

Dispose will be called on the object referenced in the argument of the using clause. It's that simple.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文