我們都知道using可以釋放實作IDisposable介面的類別,主要是釋放非托管的物件,在操作檔案的時候就必須要釋放才不會造成檔案鎖定,[.NET] 使用 using 或 try/finally 清理資源,使用
try/finally的寫法如下
public string ReaderLine(string FileName) { if (!File.Exists(FileName)) return null; Stream stream = null; StreamReader reader = null; string line = ""; StringBuilder sb = new StringBuilder(); try { stream = File.Open(FileName, FileMode.Open); reader = new StreamReader(stream); while ((line = reader.ReadLine()) != null) { sb.AppendLine(line); } return sb.ToString(); } finally { if (stream != null) stream.Close(); if (reader != null) reader.Close(); } }
使用using的寫法如下
public string ReaderLine(string FileName) { if (!File.Exists(FileName)) return null; string line = ""; StringBuilder sb = new StringBuilder(); using (Stream stream = File.Open(FileName, FileMode.Open)) { using (StreamReader reader = new StreamReader(stream)) { while ((line = reader.ReadLine()) != null) { sb.AppendLine(line); } return sb.ToString(); } } }
上述的寫法例外發生時會被吃掉嗎?
我相信很多人對上述的寫法會有疑問,我們來做個小實驗,我在try區塊以及using區塊裡面故意寫轉型失敗的程式碼,我們可以發現VS仍有拋出例外訊息。
所以不管是using還是try/finally都不會吃掉例外訊息。
基本上它們兩個是一樣的東西,先來用Reflector看看using所產生的IL,using區段變成try/finlly了
.method public hidebysig instance string ReaderLine(string FileName) cil managed { .maxstack 2 .locals init ( [0] string line, [1] class [mscorlib]System.Text.StringBuilder sb, [2] class [mscorlib]System.IO.Stream stream, [3] class [mscorlib]System.IO.StreamReader reader, [4] string a, [5] uint8 b, [6] string CS$1$0000, [7] bool CS$4$0001) L_0000: nop L_0001: ldarg.1 L_0002: call bool [mscorlib]System.IO.File::Exists(string) L_0007: stloc.s CS$4$0001 L_0009: ldloc.s CS$4$0001 L_000b: brtrue.s L_0012 L_000d: ldnull L_000e: stloc.s CS$1$0000 L_0010: br.s L_008d L_0012: ldstr "" L_0017: stloc.0 L_0018: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor() L_001d: stloc.1 L_001e: ldarg.1 L_001f: ldc.i4.3 L_0020: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Open(string, valuetype [mscorlib]System.IO.FileMode) L_0025: stloc.2 L_0026: nop L_0027: ldloc.2 L_0028: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(class [mscorlib]System.IO.Stream) L_002d: stloc.3 L_002e: nop L_002f: br.s L_003b L_0031: nop L_0032: ldloc.1 L_0033: ldloc.0 L_0034: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string) L_0039: pop L_003a: nop L_003b: ldloc.3 L_003c: callvirt instance string [mscorlib]System.IO.TextReader::ReadLine() L_0041: dup L_0042: stloc.0 L_0043: ldnull L_0044: ceq L_0046: ldc.i4.0 L_0047: ceq L_0049: stloc.s CS$4$0001 L_004b: ldloc.s CS$4$0001 L_004d: brtrue.s L_0031 L_004f: ldstr "999" L_0054: stloc.s a L_0056: ldloc.s a L_0058: call uint8 [mscorlib]System.Byte::Parse(string) L_005d: stloc.s b L_005f: ldloc.1 L_0060: callvirt instance string [mscorlib]System.Object::ToString() L_0065: stloc.s CS$1$0000 L_0067: leave.s L_008d L_0069: ldloc.3 L_006a: ldnull L_006b: ceq L_006d: stloc.s CS$4$0001 L_006f: ldloc.s CS$4$0001 L_0071: brtrue.s L_007a L_0073: ldloc.3 L_0074: callvirt instance void [mscorlib]System.IDisposable::Dispose() L_0079: nop L_007a: endfinally L_007b: ldloc.2 L_007c: ldnull L_007d: ceq L_007f: stloc.s CS$4$0001 L_0081: ldloc.s CS$4$0001 L_0083: brtrue.s L_008c L_0085: ldloc.2 L_0086: callvirt instance void [mscorlib]System.IDisposable::Dispose() L_008b: nop L_008c: endfinally L_008d: nop L_008e: ldloc.s CS$1$0000 L_0090: ret .try L_002e to L_0069 finally handler L_0069 to L_007b .try L_0026 to L_007b finally handler L_007b to L_008d }
再看看Reflector不優化IL的結果,選C# None,Reflector會幫我們直接翻譯IL成C#,當然你還可以翻成其它.NET語言
public string ReaderLine(string FileName) { string line; StringBuilder sb; Stream stream; StreamReader reader; string a; byte b; string CS$1$0000; bool CS$4$0001; if (File.Exists(FileName) != null) { goto Label_0012; } CS$1$0000 = null; goto Label_008D; Label_0012: line = ""; sb = new StringBuilder(); stream = File.Open(FileName, 3); Label_0026: try { reader = new StreamReader(stream); Label_002E: try { goto Label_003B; Label_0031: sb.AppendLine(line); Label_003B: if ((((line = reader.ReadLine()) == null) == 0) != null) { goto Label_0031; } a = "999"; b = byte.Parse(a); CS$1$0000 = sb.ToString(); goto Label_008D; } finally { Label_0069: if ((reader == null) != null) { goto Label_007A; } reader.Dispose(); Label_007A:; } } finally { Label_007B: if ((stream == null) != null) { goto Label_008C; } stream.Dispose(); Label_008C:; } Label_008D: return CS$1$0000; }
由上面的演示我們可以知道
1.using跟try/finally,都不會吃掉例外訊息,因為沒有寫catch區段。
2.using是try/finally的簡易寫法。
兩者的寫法結果都一樣,就看自己喜歡什麼樣子的寫法,using的寫法看起來比較短,若只有一層我會使用,但如果有多層using(兩層以上),我就會覺得閱讀上有很大的不便,我就不會採用這樣的寫法。