简体   繁体   English

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

[英]I don't quite understand the workings of using/Disposable objects

I asked a question regarding returning a Disposable ( IDisposable ) object from a function , but I thought that I would muddle the discussion if I raised this question there. 我问了一个关于从函数中返回一个Disposable( IDisposable )对象的问题 ,但是如果我在那里提出这个问题,我认为我会讨论这个问题。

I created some sample code: 我创建了一些示例代码:

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;
    }
}

I coded it this way deliberately, because then I call: 我故意用这种方式编码,因为我打电话给:

using (var tmp = UsingTest.GetDisposable(true)) { }

Using the debugger, I notice that the Dispose method never executes, even though we've already instantiated a Disposable object. 使用调试器,我注意到Dispose方法永远不会执行,即使我们已经实例化了一个Disposable对象。 If I correctly understand the purpose of Dispose , if this class actually had opened handles and the like, then we would not close them as soon as we had finished with them. 如果我正确理解Dispose的目的,如果这个类实际上已经打开了句柄之类的东西,那么我们就不会在完成它们之后立即关闭它们。

I ask this question because this behavior aligns with what I would expect, but in the answers to the related question, people seemed to indicate that using would take care of everything. 我问这个问题,因为这种行为符合我的期望,但在相关问题的答案中,人们似乎表明using会照顾一切。

If using still somehow takes care of all of this, could someone explain what I'm missing? 如果using仍然以某种方式照顾所有这一切,有人可以解释我错过了什么? But, if this code could indeed cause resource leak, how would you suggest I code GetDisposable (with the condition that I must instantiate the IDisposable object and run code which could throw an exception prior to the return statement)? 但是,如果这段代码确实会导致资源泄漏,你会如何建议我编写GetDisposable代码(条件是我必须实例化IDisposable对象并运行可能在return语句之前抛出异常的代码)?

The reason it's never called is because of the way you allocate it. 它从未被调用的原因是因为你分配它的方式。 The "tmp" variable is never allocated at all, because the GetDisposable(bool) function never returns due to the fact that you threw an exception. 永远不会分配“tmp”变量,因为GetDisposable(bool)函数永远不会返回,因为您抛出了异常。

If you were to say instead the following, 如果你要说以下,

using (var tmp = new Disposable())
{
    throw new ArgumentException("Blah");
}

then you would see that IDisposable::Dispose() does indeed get called. 那么你会看到IDisposable::Dispose() 确实被调用了。

The fundamental thing to understand is that the using block has to get a valid reference to the IDisposable object. 要理解的基本要点是using块必须获得对IDisposable对象的有效引用。 If some exception occurs so that the variable declared in the using block does not get assigned then you are out of luck, because the using block will have no knowledge of the IDisposable object. 如果发生某些异常使得在using块中声明的变量没有被分配,那么你运气不好,因为using块将不知道IDisposable对象。

As for returning an IDisposable object from a function, you should use a standard catch block inside of the function to call Dispose() in the event of a failure, but obviously you should not use a using block because this will dispose the object before you are ready to do so yourself. 至于从函数返回一个IDisposable对象,你应该使用函数内部的标准catch块来在发生故障时调用Dispose() ,但显然你不应该使用using块,因为这将在你之前处理对象准备好自己这样做。

Depending upon what semantics you want for GetDisposable , this is probably how I would implement it: 根据您对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;
    }
}

This is because the tmp variable is never assigned. 这是因为从未分配tmp变量。 It's something you need to be careful of with disposable objects. 使用一次性物品需要注意的事项。 A better definition for GewtDisposable would be: GewtDisposable的更好定义是:

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

    try
    {
        if (error)
            throw new Exception("Error!");
        return obj;
    }
    catch
    {
        obj.Dispose();
        throw;
    }
}

Because it ensures that obj is disposed. 因为它确保obj被处置。

The IDisposable interface simply guarantees that the class that implements it has a Dispose method. IDisposable接口只是保证实现它的类具有Dispose方法。 It does nothing in regard to calling this method. 它对调用此方法没有任何作用。 A using block will call Dispose on the object when the block is exited. 退出块时,using块将调用对象上的Dispose。

You create an IDisposable in GetDisposable but since you exit the function by throwing an exception, it never gets returned and hence tmp never gets assigned. 您在GetDisposable创建了一个IDisposable ,但由于您通过抛出异常退出该函数,因此它永远不会被返回,因此永远不会分配tmp The using statement is shorthand for using语句是简写

var tmp = UsingTest.GetDisposable(true);
try { }
finally
{
    if(tmp != null) tmp.Dispose();
}

and you never reach the try block. 而你永远不会到达try块。 The solution in your example is to check the error flag before creating the disposable obj: 您的示例中的解决方案是在创建一次性obj之前检查error标志:

public static Disposable GetDisposable(bool error)
{
    if (error)
        throw new Exception("Error!");
    return new Disposable();
}

A related question is Handling iDisposable in failed initializer or constructor and I think the answer is that if you want to avoid leaking disposable objects from a failed constructor, you'll have to smuggle out a copy of the object from the constructor (eg stash it in a passed-in container, or assign it to a passed-by-reference variable) and wrap the constructor call in a catch block. 一个相关的问题是在失败的初始化器或构造函数中处理iDisposable,我认为答案是如果你想避免从失败的构造函数中泄漏一次性对象,你将不得不从构造函数中走私对象的副本(例如存储它)在传入的容器中,或将其分配给传递的引用变量)并将构造函数调用包装在catch块中。 Icky, but I don't know how to do it better. Icky,但我不知道怎么做得更好。 VB.net can actually manage a little better than C# because of how its initializers work. VB.net实际上可以比C#管理得更好,因为它的初始化程序的工作方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM