對於Try catch finally,大家應該都不陌生,您接觸的寫法可能會是下面的記幾種類型:
Try catch (您可以匹配多個catch)
try
{
}
catch (Exception)
{
throw;
}
Try finally
try
{
}
finally
{
}
Try catch finally (同樣,你一樣可以匹配多個catch)
Try catch finally
try
{
}
catch (ArgumentNullException e)
{ }
catch (Exception ex)
{ }
finally
{
}
在這裡,finally的作用簡單的一句話說就是“無論try裡面的代碼正常執行或者發生異常,都會繼續執行finally裡面的代碼”,所以我們一般會在finally裡面執行我們的一些清理操作。尤其對於操作一些非托管資源或者比較珍貴的資源的時候,執行必要的清理操作顯得尤為重要,具體的解釋您可以參考MSDN。
說了這些,我們來看看try finally,不知道您平時是使用try finally,還是會使用更簡潔的語法using {}。對於using, 我這裡並不是想詳細的解釋它的用法,如果您想了解,您請看這裡。我們都知道using只是為了讓語法變的更簡潔而已,我不知道在這裡用語法糖這個詞來形容它是否合適。為了驗證try finally和using是否一致,我再次查看了編譯之後的代碼(這裡我還是使用MSDN的例子):
代碼
{
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}
我們看一下編譯之後的結果(我只是截取了有用的部分代碼):
using
.try
{
IL_0012: nop
IL_0013: ldloc.0
IL_0014: callvirt instance uint8 [System.Drawing]System.Drawing.Font::get_GdiCharSet()
IL_0019: stloc.1
IL_001a: nop
IL_001b: leave.s IL_002f
} // end .try
finally
{
IL_001d: nop
IL_001e: ldloc.0
IL_001f: ldnull
IL_0020: ceq
IL_0022: stloc.3
IL_0023: ldloc.3
IL_0024: brtrue.s IL_002d
IL_0026: ldloc.0
IL_0027: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002c: nop
IL_002d: nop
IL_002e: endfinally
} // end handler
using
.try
{
IL_0041: nop
IL_0042: ldloc.0
IL_0043: callvirt instance uint8 [System.Drawing]System.Drawing.Font::get_GdiCharSet()
IL_0048: stloc.1
IL_0049: nop
IL_004a: leave.s IL_005c
} // end .try
finally
{
IL_004c: ldloc.0
IL_004d: ldnull
IL_004e: ceq
IL_0050: stloc.2
IL_0051: ldloc.2
IL_0052: brtrue.s IL_005b
IL_0054: ldloc.0
IL_0055: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_005a: nop
IL_005b: endfinally
} // end handler
沒什麼區別,不是嗎?到這裡我產生兩個疑問,1. 那麼對於try catch和try catch finally編譯之後會是什麼樣子呢? 2. 如果using裡面的代碼產生異常怎麼辦呢?於是我編譯了try catch finally代碼:
Try catch finally
.try
{
.try
{
IL_006e: nop
IL_006f: ldloc.0
IL_0070: callvirt instance uint8 [System.Drawing]System.Drawing.Font::get_GdiCharSet()
IL_0075: stloc.1
IL_0076: nop
IL_0077: leave.s IL_007e
} // end .try
catch [mscorlib]System.Object
{
IL_0079: pop
IL_007a: nop
IL_007b: nop
IL_007c: leave.s IL_007e
} // end handler
IL_007e: nop
IL_007f: leave.s IL_0093
} // end .try
finally
{
IL_0081: nop
IL_0082: ldloc.0
IL_0083: ldnull
IL_0084: ceq
IL_0086: stloc.2
IL_0087: ldloc.2
IL_0088: brtrue.s IL_0091
IL_008a: ldloc.0
IL_008b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0090: nop
IL_0091: nop
IL_0092: endfinally
} // end handler
你仔細看上面的代碼,編譯器把try catch finally塊分解了,新增加了一個try,把try catch塊放在 裡面,外面套嵌了finally,看來雖然我們自己把try catch finally三者寫在一起,當成一個"塊",但是編譯器會分開處理。
那麼對於using產生異常的時候會怎麼樣呢?我們看下面的代碼:
隨便定義一個類
public class Test : IDisposable
{
public int ID;
#region IDisposable Members
public void Dispose()
{
Console.WriteLine(".......................");
}
#endregion
}
using (var test = new Test())
{
test.ID = Int32.Parse("a");
}
上面的代碼執行肯定會拋出異常,但是您如果強制繼續執行的話,你會看到"............................" 的輸出。我們的目的達到了,雖然程序產生了異常,但是我們的清理資源的代碼還是執行了。但是這會出來一個很不友好的界面,告訴你程序掛掉了。我想任何一個用戶都不希望看到這個。那我們怎麼辦呢?是繼續用代碼貌似不夠簡潔的Try catch finally還是在using裡面加上catch來捕獲異常呢?寫成這種形式嗎?
加強版的using
using (var test = new Test())
{
try
{
test.ID = Int32.Parse("a");
}
catch (Exception) { }
}
這樣的語法編譯出來和Try catch finally是一樣的效果,但是代碼要簡潔一些。但是每次用using我都需要自己手動加上上面的代碼,讓我覺得很不好。還好vs裡面using是通過 Code Snippets來實現的,所以我考慮可以在這裡面動一動手腳了。於是我添加了一個新的Snippets文件,將using裡面的內容拷過來,修改如下:
usingTry
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>usingTry</Title>
<Shortcut>usingTry</Shortcut>
<Description>Code snippet for using statement add Try</Description>
<Author>Microsoft Corporation</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>resource</ID>
<ToolTip>Resource to use</ToolTip>
<Default>resource</Default>
</Literal>
<Literal>
<ID>expression</ID>
<ToolTip>Exception type</ToolTip>
<Function>SimpleTypeName(global::System.Exception)</Function>
</Literal>
</Declarations>
<Code Language="csharp"><![CDATA[
using($resource$)
{
try
{
$selected$
}
catch ($expression$)
{
$end$
}
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
至此,完成了一個加強版的using,和使用普通的using是一樣的。