【问题标题】:IDisposable: is it necessary to check for null on finally {}?IDisposable:是否有必要在 finally {} 上检查 null?
【发布时间】:2010-04-08 05:53:43
【问题描述】:

在您在网络上找到的大多数示例中,当明确不使用“使用”时,模式看起来类似于:

  SqlConnection c = new SqlConnection(@"...");
  try {
    c.Open();
  ...
  } finally {
    if (c != null) //<== check for null
      c.Dispose();
  }

如果你确实使用了“使用”并查看生成的 IL 代码,你可以看到它生成了对 null 的检查

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

【问题讨论】:

  • 在使用/尝试块中将变量设置为空的情况下,是否可以防止出现空引用异常?
  • 我就是这么想的
  • 好问题..让我拿出我的代码涂鸦板并学习新东西。

标签: c# .net idisposable coding-style


【解决方案1】:

“using”语句可以使用构造函数以外的调用来初始化变量。例如:

using (Foo f = GetFoo())
{
    ...
}

这里的f 很容易为null - 而构造函数调用永远不会1 返回null。这就是为什么using 语句会检查无效性的原因。这与块本身内部的内容无关,因为using 语句保留了原始初始值。如果你写:

Stream s;
using (s = File.OpenRead("foo.txt"))
{
    s = null;
}

那么流仍将被释放。 (如果变量在using 语句的初始化部分中声明,则无论如何它都是只读的。)

在您的情况下,正如您在进入 try 块之前知道 c 是非空的,您不需要检查 finally 块中的空值,除非您重新分配它的值(我真诚地希望你不是!)在街区内。

现在使用您当前的代码 在分配给 c 和进入 try 块之间可能会引发异步异常的轻微风险 - 但很难避免这种情况完全竞争条件,因为在构造函数完成之后但在将值分配给 c 之前同样可能存在异步异常。我建议大多数开发人员不必担心这类事情 - 异步异常往往足够“难”,无论如何它们都会降低进程。

你有什么理由不想只使用 using 语句吗?老实说,这些天我很少写自己的finally 块...


1 看到 Marc 的回答并哭泣。但通常不相关。

【讨论】:

  • 是的,在这种特定情况下,使用 using 语句是有原因的,通常我使用 using 并且不用担心
  • 我的具体案例要么创建一个 SqlConnection 实例,要么获取一个活动实例并使用它,最后我必须处理它以防它不是“活动实例”
  • "在你的情况下,你知道 c 在进入 try 块之前是非空的,你不需要在 finally 块中检查 null 除非你重新分配它的值(我真诚地希望你不是!)在街区内。”这回答了我的问题,谢谢!
  • (重新编辑)大声笑 - 但是记录在案,没有任何 sane 代码应该担心这个;-p
【解决方案2】:

在您的示例中,检查 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(); 
  } 

【讨论】:

  • 你不知道他代码中的“...”部分不是“c = null;”所以检查是必要的。
  • @Matt - 没错,但他大概知道那里有什么。
  • 我的感觉是,因为他是从代码示例中阅读的,所以他们可能也有“...”,而作者没有任何机会。
  • 我用...只是为了消除连接字符串的噪音,没必要说明重点
【解决方案3】:

Jon 已经介绍了这里的主要观点……但只是一个随机的琐事;您的 构造函数 可能会返回 null。不是很常见的情况,当然也不是using 这样做的真正原因,你真的不应该为此折磨你的代码 - 但是it can happen(寻找MyFunnyProxy)。

【讨论】:

  • 如果无法创建 SqlConnection 实例,会不会只是抛出异常?
  • 查看 Jon 的评论:“而构造函数调用永远不能返回 null”
  • @BlackTigerX ,@Jon 说真的,这只是一个“仅供参考”。 不要仅仅因为这个疯狂的边缘案例而折磨代码。
【解决方案4】:

有趣.. 我正在使用 VS2010,我发现通过我的自定义代码 sn-ps (R :) - 使用块岩石。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    • 1970-01-01
    • 2012-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    相关资源
    最近更新 更多