我不太了解使用/ Disposable对象的工作原理

pal*_*wim 6 c# idisposable using-statement

我问了一个关于从函数中返回一个Disposable(IDisposable)对象的问题,但是如果我在那里提出这个问题,我认为我会讨论这个问题.

我创建了一些示例代码:

class UsingTest
{
    public class Disposable : IDisposable
    {
        public void Dispose()
        {
            var i = 0;
            i++;
        }
    }
    public static Disposable GetDisposable(bool error)
    {
        var obj = new Disposable();
        if (error)
            throw new Exception("Error!");
        return obj;
    }
}
Run Code Online (Sandbox Code Playgroud)

我故意用这种方式编码,因为我打电话给:

using (var tmp = UsingTest.GetDisposable(true)) { }
Run Code Online (Sandbox Code Playgroud)

使用调试器,我注意到该Dispose方法永远不会执行,即使我们已经实例化了一个Disposable对象.如果我正确地理解了这个目的Dispose,如果这个类实际上已经打开了句柄之类的东西,那么我们一旦完成它们就不会关闭它们.

我问这个问题是因为这种行为符合我的期望,但在相关问题的答案中,人们似乎表明using会照顾一切.

如果using仍然以某种方式处理所有这些,有人可以解释我错过了什么?但是,如果这段代码确实会导致资源泄漏,你会如何建议我编写代码GetDisposable(条件是我必须实例化IDisposable对象并运行可能在return语句之前抛出异常的代码)?

Gre*_*ley 11

它从未被调用的原因是因为你分配它的方式.永远不会分配"tmp"变量,因为GetDisposable(bool)函数永远不会返回,因为您抛出了异常.

如果你要说以下,

using (var tmp = new Disposable())
{
    throw new ArgumentException("Blah");
}
Run Code Online (Sandbox Code Playgroud)

然后你会看到IDisposable::Dispose() 确实被调用了.

要理解的基本要素是using块必须获得IDisposable对象的有效引用.如果发生了一些异常,因此using块中声明的变量没有被赋值,那么你就不幸了,因为using块不知道该IDisposable对象.

至于IDisposable从函数返回一个对象,你应该使用catch函数内部的标准块来Dispose()在发生故障时调用,但显然你不应该使用一个using块,因为这会在你准备好之前处理对象.


str*_*ger 4

根据您想要的语义GetDisposable,这可能是我实现它的方式:

public static Disposable GetDisposable(bool error)
{
    var obj = new Disposable();

    try
    {
        if (error)
            throw new Exception("Error!");

        return obj;
    }
    catch (Exception)
    {
        obj.Dispose();
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)