簡體   English   中英

C#:using block:對象重新初始化

[英]C#: using block: object re-initialization

在“使用”塊內重新初始化是一個壞主意,在任何時候都要避免。 我還是會問這個:

為什么“使用”調用處理原始值而不是最后一個引用或重新初始化(如果使用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();
  }
}

C#規范中定義了兩種using語句形式:

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

如果您有local-variable-declaration ,則不會有任何問題。 該變量在使用塊中是只讀的,您根本無法更改它。 規范說:

8.13使用聲明

[...]在任一擴展中,資源變量在嵌入語句中是只讀的。

在這里,我們正在處理第二種形式: resource-acquisitionexpression而不是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 都? 都不是? 目前的行為是有道理的。

使用可以看作是對使用聲明聲明的對象進行調用的承諾。 這是恕我直言,唯一有意義的事情!

如果您在重新分配的值上調用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局部變量實現了合同。

似乎“使用”是創建它自己的變量來存儲引用,它被初始化為“a”,而“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具有誤導性。

將在using子句的參數中引用的對象上調用Dispose。 就這么簡單。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM