在明确未使用“使用”时,您在网络上找到的大多数示例中,模式如下所示:
SqlConnection c = new SqlConnection(@"...");
try {
c.Open();
...
} finally {
if (c != null) //<== check for null
c.Dispose();
}
如果您确实使用“使用”并查看生成的IL代码,您可以看到它生成了空检查
L_0024: ldloc.1
L_0025: ldnull
L_0026: ceq
L_0028: stloc.s CS$4$0000
L_002a: ldloc.s CS$4$0000
L_002c: brtrue.s L_0035
L_002e: ldloc.1
L_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0034: nop
L_0035: endfinally
我理解为什么IL被翻译为检查null(不知道你在using块中做了什么),但是如果你正在使用try..finally并且你可以完全控制IDisposable对象的使用方式在try..finally块里面,你真的需要检查null吗?如果是这样,为什么?
答案 0 :(得分:12)
“using”语句可以使用构造函数以外的调用来初始化变量。例如:
using (Foo f = GetFoo())
{
...
}
这里f
很容易为空 - 而构造函数调用永远不会 1 返回null。这就是using
语句检查无效的原因。这与块本身内部的内容无关,因为using
语句保留了原始初始值。如果你写:
Stream s;
using (s = File.OpenRead("foo.txt"))
{
s = null;
}
然后流仍然会被处理掉。 (如果变量在using
语句的初始化部分中声明,则无论如何都是只读的。)
在您的情况下,如您所知,c
在您输入try
块之前是非空的,您不需要在finally
块中检查空值,除非您“重新分配它的价值(我真心希望你不是!)。”
现在使用您当前的代码 可能会在分配到c
和进入try
块之间抛出异步异常 - 但很难完全避免这种竞争条件,因为在构造函数完成之后但在将值分配给c
之前,同样可能存在异步异常。我建议大多数开发人员不需要担心这类事情 - 异步异常往往足够“难”,无论如何他们都会打倒这个过程。
你有什么理由不想只使用using语句吗?说实话,这些天我很少写自己的finally
块......
1 见Marc的回答并哭泣。虽然通常不相关。
答案 1 :(得分:5)
在您的示例中,检查null是不必要的,因为在显式构造之后c不能为null。
在下面的示例中(类似于using语句生成的内容),检查null当然是必要的:
SqlConnection c = null;
try {
c = new SqlConnection(@"...");
c.Open();
...
} finally {
if (c != null) //<== check for null
c.Dispose();
}
此外,在以下情况下需要检查null,因为您无法确定CreateConnection不会返回null:
SqlConnection c = CreateConnection(...);
try {
c.Open();
...
} finally {
if (c != null) //<== check for null
c.Dispose();
}
答案 2 :(得分:2)
Jon已经涵盖了这里的主要观点......但只是一个随机的琐事;您的构造函数可能会返回null
。这不是一个常见的情况,当然不是using
这样做的真正原因,而且你真的不应该折磨你的代码 - 但是it can happen(寻找{ {1}})。
答案 3 :(得分:0)
有趣..我正在使用VS2010,我发现通过我的自定义代码片段(R :) - 使用块摇滚。