A confusing fact about using statement
Using语句提供简洁的语法来保证资源的回收,然而并不是所有的人都懂得using语句的实现。
在写篇Blog之前,我在Google上Search一下,发现了如下的文章:
http://www.codeproject.com/csharp/TinguUsingStatement.asp
不幸的是这片文章中有一个严重的错误。
在往下读之前,请先看看下面的Code有什么问题:
RegistryKey typeNameKey = null;
using (typeNameKey) {
typeNameKey = Registry.ClassesRoot.CreateSubKey(".ABCD");
typeNameKey.SetValue("", "Test");
}
// continue to do some work
如果执行这段代码,你会发现在Using Block结束后RegistryKey没有被回收(Dispose)
为什么? 让我们看看IL Code:
IL_0000: ldnull
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: stloc.1
.try
{
IL_0004: ldsfld class [mscorlib]Microsoft.Win32.RegistryKey [mscorlib]Microsoft.Win32.Registry::ClassesRoot
IL_0009: ldstr ".ABCD"
IL_000e: callvirt instance class [mscorlib]Microsoft.Win32.RegistryKey [mscorlib]Microsoft.Win32.RegistryKey::CreateSubKey(string)
IL_0013: stloc.0
IL_0014: ldloc.0
IL_0015: ldstr ""
IL_001a: ldstr "Test"
IL_001f: callvirt instance void [mscorlib]Microsoft.Win32.RegistryKey:SetValue(string,
object)
IL_0024: leave.s IL_0030
} // end .try
finally
{
IL_0026: ldloc.1
IL_0027: brfalse.s IL_002f
IL_0029: ldloc.1
IL_002a: callvirt instance void [mscorlib]System.IDisposable:Dispose()
IL_002f: endfinally
} // end handler
注意黑体部分,在finally block中回收的是using block中的代码执行前typeNameKey的值。
如果该值为空则直接跳出finally block.
所以在使用using语句时要在using语句中初始化变量。正确的代码如下:
using (RegistryKey typeNameKey = Registry.ClassesRoot.CreateSubKey(".ABCD")) {
typeNameKey.SetValue("", "Test");
}
同时注意在using block要避免改变using语句中所使用的变量的值。
2004年4月19日 6:27
Details at http://blog.joycode.com/gangp/archive/2004/04/19/19852.aspx