2010-04-08 22 views
11

在你的网络上找到的时候明确不使用“使用”最例子,图案看起来类似:IDisposable:是否有必要在finally {}上检查null?

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检查(不知道你使用的块里面做了什么),但如果你使用try ..最后,你完全控制了如何在try..finally bl中使用IDisposable对象ock,你真的需要检查null吗?如果是这样,为什么?

+4

在使用/ try块中将变量设置为null的情况下,是否可以防止出现空引用异常? – Gishu 2010-04-08 05:56:24

+0

这就是我在想 – BlackTigerX 2010-04-08 05:57:12

+0

好问题..让我走出我的代码涂鸦板,并学习新的东西。 – Gishu 2010-04-08 06:44:48

回答

12

“使用”的语句可以初始化比其他构造函数调用的变量。例如:

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

这里f很容易被空 - 而一个构造函数调用绝不能返回null。这就是为什么using声明检查无效。这与块内部没有关系,因为using语句保留了原始初始值。如果你写:

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

然后流仍然会被丢弃。 (如果变量是using语句的初始化部分中声明,它的只读反正。)

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

现在与您现有的代码有轻微的风险,即异步异常可以在分配之间掀翻c并进入try块 - 但很难避免这种竞争状态完全,因为有可能同样在构造函数完成之后但在将值分配给c之前是异步异常。我建议大多数开发人员不需要担心这种事情 - 异步异常往往足够“困难”,无论如何他们都会推迟这个过程。

是否有任何理由你不想仅仅使用using语句?说实话,我很少写这些天我自己finally块...


见马克的回答和哭泣。但通常不相关。

+0

是的,在这种特定情况下,有一个原因*不*使用using语句,通常我使用和不使用担心它 – BlackTigerX 2010-04-09 15:09:12

+0

我的具体情况要么创建一个SqlConnection的实例,要么采取一个实例并使用它,最后我必须处置以防万一它不是一个“实例” – BlackTigerX 2010-04-09 15:12:11

+0

“就你而言,在你输入try块之前c是非空的,你不需要在finally块中检查null,除非你在块中重新赋值它的值(我真心希望你不是!)。“这回答我的问题,谢谢! – BlackTigerX 2010-04-09 15:13:04

5

在你的例子中,检查null是不必要的,因为在显式构造之后c不能为空。

在下面的例子中(类似于由using语句生成的内容),对于空的检查当然会是必要的:

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

此外,空检查将在下面的需要情况下,你无法确定创建连接不会返回null:

SqlConnection c = CreateConnection(...); 
    try { 
    c.Open(); 
    ... 
    } finally { 
    if (c != null) //<== check for null 
     c.Dispose(); 
    } 
+0

你不知道他的代码的“...”部分不是“c = null”所以检查是必要的。 – 2010-04-08 06:03:02

+0

@Matt - 真的,但他大概知道那里有什么。 – Joe 2010-04-08 06:07:07

+0

我的感觉是因为他是从代码示例读这篇文章,他们可能有“......”太和作者都不敢掉以轻心。 – 2010-04-08 06:24:58

2

乔恩已经涵盖了这里的主要观点......但只是一个随机的琐事;您的构造函数可能会返回null。不是真正的常见情况,当然不是真正的原因,using这样做,你真的不应该酷刑你的代码 - 但it can happen(寻找MyFunnyProxy)。

+0

不会只是抛出一个异常,如果它是不能够创造的SqlConnection的实例? – BlackTigerX 2010-04-09 15:11:01

+0

检查乔恩的评论:“而构造函数调用永远不能返回null” – BlackTigerX 2010-04-09 15:20:48

+0

@BlackTigerX:是的,我需要认真编辑:) – 2010-04-09 15:29:22

0

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

  • 在使用块中,您不能为块变量分配null(或任何其他内容)。它会产生一个compiler error CS1656
  • 接下来通过工厂方法分配块var,该方法返回null。在这种情况下,空的使用块智能地不会调用Dispose。 (显然,如果你试图使用块var,你会得到一个NullReferenceException)
  • 接下来在try块中,没有规则,你可以给变量赋空值。因此在Dispose之前的空检查在finally块中是强制的。
相关问题