对于Try catch finally,大家应该都不陌生,您接触的写法可能会是下面的记几种类型:
Try catch (您可以匹配多个catch)
{
}
catch (Exception)
{
throw;
}
Try finally
{
}
finally
{
}
Try catch finally (同样,你一样可以匹配多个catch)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
}
catch (ArgumentNullException e)
{ }
catch (Exception ex)
{ }
finally
{
}
在这里,finally的作用简单的一句话说就是“无论try里面的代码正常执行或者发生异常,都会继续执行finally里面的代码”,所以我们一般会在finally里面执行我们的一些清理操作。尤其对于操作一些非托管资源或者比较珍贵的资源的时候,执行必要的清理操作显得尤为重要,具体的解释您可以参考MSDN。
说了这些,我们来看看try finally,不知道您平时是使用try finally,还是会使用更简洁的语法using {}。对于using, 我这里并不是想详细的解释它的用法,如果您想了解,您请看这里。我们都知道using只是为了让语法变的更简洁而已,我不知道在这里用语法糖这个词来形容它是否合适。为了验证try finally和using是否一致,我再次查看了编译之后的代码(这里我还是使用MSDN的例子):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}
我们看一下编译之后的结果(我只是截取了有用的部分代码):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
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代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
.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产生异常的时候会怎么样呢?我们看下面的代码:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
{
public int ID;
#region IDisposable Members
public void Dispose()
{
Console.WriteLine(".......................");
}
#endregion
}
{
test.ID = Int32.Parse("a");
}
上面的代码执行肯定会抛出异常,但是您如果强制继续执行的话,你会看到"............................" 的输出。我们的目的达到了,虽然程序产生了异常,但是我们的清理资源的代码还是执行了。但是这会出来一个很不友好的界面,告诉你程序挂掉了。我想任何一个用户都不希望看到这个。那我们怎么办呢?是继续用代码貌似不够简洁的Try catch finally还是在using里面加上catch来捕获异常呢?写成这种形式吗?
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
{
try
{
test.ID = Int32.Parse("a");
}
catch (Exception) { }
}
这样的语法编译出来和Try catch finally是一样的效果,但是代码要简洁一些。但是每次用using我都需要自己手动加上上面的代码,让我觉得很不好。还好vs里面using是通过Code Snippets来实现的,所以我考虑可以在这里面动一动手脚了。于是我添加了一个新的Snippets文件,将using里面的内容拷过来,修改如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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是一样的。