IDisposable:是否有必要在最后{}检查null?

时间:2010-04-08 05:53:43

标签: c# .net idisposable coding-style

在明确未使用“使用”时,您在网络上找到的大多数示例中,模式如下所示:

  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吗?如果是这样,为什么?

4 个答案:

答案 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 :) - 使用块摇滚。

  • 在使用块中,您不能将null(或任何内容)分配给块变量。它会产生compiler error CS1656
  • 接下来通过返回null的工厂方法分配块var。在这种情况下,智能地使用空块不会调用Dispose。 (显然,如果你尝试使用block var,你会得到NullReferenceException)
  • 接下来在try块中,没有规则,您可以为变量赋值null。因此,在finally块中必须使用Dispose之前进行空检查。