Does C# automatically “dispose” IDisposable objects when they fall out of scope?

不羁的心 提交于 2021-02-16 20:04:34

问题


I am writing an ASP.NET Web API. As part of this, I have a class that I found out, during testing, needs to implement IDisposable to ensure managed resources are freed.

So, I implemented IDisposable in my class, and put the code necessary to free the resources in the Dispose() method.

There are many places in my code (hundreds) where I instantiate this object, and in the same line call a method on the new instance. I only need the instance to call the single method.

Here's an example:

// MyObject, a class that needs to be disposed of.
public class MyObject : IDisposable
{
    private AnObjectThatMustBeDisposed localObject;

    public MyObject() 
    {
        localObject = SomeLibrary.SomeProject.AnObjectThatMustBeDisposed.Create();
    }

    public void doOperationOne()
    {
        localObject.DoSomething(1);
    }

    public string getOperationTwo()
    {
        return localObject.DoSomething(2);
    }

    public string getOperationThree()
    {
        return localObject.DoSomething(3);
    }

    public bool getOperationFour(string input)
    {
        return localObject.DoSomethingSpecial(4,input.ToLower());
    }

    ...

    public void getOperationOneHundred(DateTime input)
    {
        localObject.DoSomethingElse(100,input);
    }

    public void Dispose()
    {
        localObject.CloseResources();
        localObject.FreeUpMemory();
        localObject.Close();
        localObject.Dispose();
    }
}

// A class that makes use of MyObject
public class MyLibraryThatUsesMyObject
{
    public void Method1()
    {
        new MyObject().doOperationOne();
    }
    public string Method2()
    {
        return new MyObject().getOperationTwo();
    }
    public int Method3()
    {
        return new MyObject().getOperationThree();
    }
    public bool Method4(string testString)
    {
        if (testString.Length > 6)
        {
            if (new MyObject().getOperationFour(testString)) return true;
            else return false;
        }
        else return false;
    }

    ...

    public void Method100()
    {
        new MyObject().doOperationOneHundred(DateTime.Now);
    }
}

My question is: Does .NET automatically Dispose() objects when they fall out of scope? Or, do I actually have to do this...

public void Method1() 
{
    using (MyObject o = new MyObject())
    {
        o.DoOperationOne();
    }
}

...to each method? It wouldn't be hard if I had two or three methods, but if I have tons of methods, this refactoring could take quite a while.

I am not sure how ASP.NET handles requests as they complete - i.e. does the framework give code time to Dispose() things, or does it "cut off" execution as soon as the return is called, not letting things dispose?

The fact that, without implementing IDisposable myself, things inside the MyObject class are failing due to unreleased resources causing leaks, it feels like .NET does not automatically Dispose things. So, if that's the case, can I do something so I don't have to refactor hundreds of methods?


EDIT: I tried simply implementing IDisposable, but my unit test was still able to produce a resource leak. So it would appear that my suspicion that .NET is not automatically disposing is correct. So now my question becomes - how can I force disposing without having to refactor hundreds of methods?


回答1:


Dispose is not automatically called. If you don't call .Dispose() (either explicitly or via a using statement) the method will never be called.

The only caveat is methods that are implemented with the pattern

public void Dispose()
{
    GC.SuppressFinalize(this);
    Dispose(true);
}

~MyClass()
{
    Dispose(false);
}

bool _isDisposed = false;
protected virtual void Dispose(bool disposeing)
{
    if(_isDisposed)
        return;

    _isDisposed = true;

    if(disposing)
    {
        //Disposed managed code here
    }

    //Dispose unmanaged code only here.
}

Will have Dispose(false) called on it when the object is finalized, but you are not allowed to dispose (or even access) managed objects (i.e: other stuff that implements .Dispose()) when disposing is false.

You will need to refactor your code if you want your resources disposed correctly.

There is a really, really, good article written by Stephen Cleary "IDisposable: What Your Mother Never Told You About Resource Deallocation" that does a very good job explaining how Dispose works and how to correctly write your own disposeable objects (for example, that caveat pattern I mentioned above is recommended by Microsoft but is actually a very bad pattern to do. Classes should only either hold only managed resources or derive from SafeHandle and only hold a unmanaged resource and possibly other SafeHandles. You should never have a class that holds both managed and unmanaged resources nor a single class that holds multiple unmanaged resources)




回答2:


I am not sure how ASP.NET handles requests as they complete - i.e. does the framework give code time to Dispose() things, or does it "cut off" execution as soon as the return is called, not letting things dispose?

My answer will try to answer the specific question above since you have gotten answers to your other question.

In the MVC framework there is a class that creates controllers. It is named DefaultControllerFactory and it has this method:

public virtual void ReleaseController(IController controller)
{
    IDisposable disposable = controller as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }

}

Please note the parameter the above method takes: IController. So it passes your controller class (since all controllers implement that interface by deriving the BaseController) and if your controller implements IDisposable, it will cast it and then call Dispose() on it. So it gives you a chance to do whatever cleanup you need to do.

If you have rolled your own factory and you are not using the DefaultControllerFactory then make sure you are doing the same thing and calling the Dispose on the controllers.

I got the above source code from MVC's Source Code.




回答3:


No, the CLR will never call Dispose on your object when it goes out of scope. It will, however, call the class destructor (which must be explicitly overridden).

Note: The example that follows is not intended to demonstrate what should be done in production code. The practice recommended by Microsoft can be found here.

Consider the following class:

public class MyDisposable : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing this disposable instance.");
    }

    // Note: This is not the right way to implement the dispose pattern. See
    // the MSDN docs on the recommended pattern.
    ~MyDisposable()
    {
        Dispose();
    }
}

Now, write this method that uses it, and set a break point on the MyDispoable.Dispose method.

private static void UseDisposable()
{
    Console.WriteLine("Enter");
    var disposable = new MyDisposable();
    Console.WriteLine("Exit");
}

What you will see is that, when leaving this scope, not only is the Dispose on MyDisposable not called, but neither is the finalizer. This means that the locals you created in your method are not cleaned up at the end of the method, even though they are local to the method (not returned to the caller, or assigned to any other object).

The way that I try to manage disposable resources in my own code is by not using them as class members, and put all of them inside of using blocks. Keep in mind that a using is only syntax sugar for try {...} finally {...}, though.

Now, I think your question is more about ASP.NET than the CLR. I am not sure of what ASP.NET does to handle disposable resources. If you are using the resource inside of a Controller, then have a go at implementing IDisposable, and using a break point to see if it gets called after your method completes. That's a scenario that I'm not as sure about, but it seems reasonable that it may work that way.

Edit: See the answer from @CodingYoshi regarding ASP.NET controller disposal. It turns out that MVC will deterministically call dispose, so some of your refactoring may be slightly more simple.



来源:https://stackoverflow.com/questions/43420323/does-c-sharp-automatically-dispose-idisposable-objects-when-they-fall-out-of-s

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!