如果using语句抛出异常,我该如何处理一个IDisposable对象的释放?

4

如何在以下代码片段中确保IDataReaderExecuteReader抛出异常时被处理?

using (IDataReader rdr = cmd.ExecuteReader())
{
    // use it
}

我认为使用using语法糖不调用Dispose(因为没有实例可调用它)。然而,我如何确保实现IDisposable接口的类通常分配的稀缺资源将被释放?


5
如果 ExecuteReader 抛出异常,IDataReader 是 null,并且不需要处理。ExecuteReader 内部的非托管资源需要清理,并应在其自己的 finally 块中处理。 - cjk
4个回答

14
如果在你的示例中,ExecuteReader 抛出异常,则不会返回任何内容。此时,由 ExecuteReader 的实现来处理在异常之前创建的任何东西。

那么,如果抛出异常,清理工作就成为异常抛出者的责任了? - jpoh
3
@jpoh说:“这是必须的;因为调用者没有任何对象可尝试清理。最坏的情况下,终结器会启动。” - Marc Gravell

3

如果对象的构造函数运行失败,则您不需要处理任何需要被处理的对象。

如果您正在编写可能会抛出异常的构造函数,最好使用using或try-catch块来清理您需要的任何内容。

在您的IDataReader示例中,如果cmd.ExecuteReader()方法调用失败,仅需处理命令对象即可。


只是为了澄清Matt所说的,使用/在您的一次性类构造函数内使用try catch来使其正确处理其自己的内部资源。 我更倾向于try catch,因为资源可能是类级别的资源,而不仅仅是构造函数级别的资源。 如果是这样,那么使用更合适。 - Jimmy Chandra

0
把初始化代码从构造函数中移出来,放到一个单独的函数中如何?这样你就会有:
using (var tc = new TestClass())
{
    tc.Initialize();  // setup the object. acquire expensive resources here, etc.

    ..more code ..
}

这不是必需的。同样,如果构造函数抛出异常,则没有需要清理的内容。 - John Saunders

-2

我认为using语句被翻译成类似于IL的代码:

try
{
}
finally
{
    disposableUsedObject.Dispose();
}

所以,在正常情况下,我认为应该调用Dispose方法?

此外,在构造函数中不应抛出异常,因为用户不会预期在实例化对象时会抛出异常。 我会将可能引发异常的初始化逻辑移动到另一个方法(Initialize)中,在实例化对象后必须调用该方法。


1
但是如果构造函数抛出了异常,那么就没有对象可供处理? - cjk
在构造函数中抛出异常是有点出乎意料的,因此不应该这样做。类的用户并不希望在构造函数/实例化对象时会抛出异常。我认为最好将可能引发异常的初始化逻辑移动到另一个方法(例如Initialize)中。 - Frederik Gheysels
抱歉,我的人为例子似乎让这个问题变得混乱了。我会编辑问题以使其更清晰。 - jpoh
ck是正确的。我使用简单的控制台应用程序进行了测试,当从构造函数抛出错误时,dispose将不会被调用。 - Jimmy Chandra
@Jimmy:由于没有对象需要处理,因此Dispose方法不会被调用! - John Saunders

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接