[英]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-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
? 都? 都不是? 目前的行為是有道理的。
使用可以看作是對使用聲明聲明的對象進行調用的承諾。 這是恕我直言,唯一有意義的事情!
如果您在重新分配的值上調用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.