当C#超出范围时,C#会自动“处置”它们吗?

时间:2017-04-14 23:04:45

标签: c# asp.net dispose

我正在编写一个ASP.NET Web API。作为其中的一部分,我在测试期间发现了一个类,需要实现IDisposable以确保释放托管资源。

所以,我在我的课程中实现了IDisposable,并在Dispose()方法中放置了释放资源所需的代码。

我的代码中有很多地方(数百个)我实例化这个对象,并在同一行中调用新实例上的方法。我只需要实例来调用单个方法。

以下是一个例子:

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

我的问题是:当.NET超出范围时,.NET会自动Dispose()对象吗?或者,我真的必须这样做......

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

...对每种方法?如果我有两三种方法就不难,但如果我有很多方法,这种重构可能需要一段时间。

我不确定ASP.NET在完成请求时如何处理请求 - 即框架是否为Dispose()事物提供了代码时间,或者return是否“切断”执行叫,不要让事情处理?

事实上,如果没有自己实现IDisposableMyObject类中的内容由于未释放的资源导致泄漏而失败,感觉.NET自动 { {1}}事情。所以,如果是这样的话,我可以做一些事情,所以我不必重构数百种方法吗?

编辑:我尝试过简单地实现Dispose,但我的单元测试仍然能够产生资源泄漏。因此,我怀疑.NET不会自动处理是正确的。所以现在我的问题变成了 - 如何强制处理而不必重构数百种方法?

3 个答案:

答案 0 :(得分:3)

不会自动调用Dispose。如果您不打电话.Dispose()(显式或通过using语句),则永远不会调用此方法。

唯一需要注意的是使用模式

实现的方法
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.
}

当对象完成时,将Dispose(false)调用它,但是当{{1}时,不允许您处置(甚至访问)托管对象(即:实现.Dispose()的其他内容)是} disposing

如果您希望正确处理资源,则需要重构代码。

有一篇真正的,真的,由Stephen Cleary编写的好文章" IDisposable: What Your Mother Never Told You About Resource Deallocation"这可以很好地解释Dispose如何工作以及如何正确编写自己的可处置对象(例如,我上面提到的那个告诫模式是微软推荐的,但实际上是一个非常糟糕的模式。类应该只保持托管资源或派生自false并且只保存非托管资源,可能还有其他SafeHandle。您永远不应该拥有一个既包含托管资源又包含非托管资源的类,也不应该拥有包含多个非托管资源的单个类。

答案 1 :(得分:3)

  

我不确定ASP.NET在完成请求时如何处理请求 - 即框架是否为Dispose()提供了代码时间,或者在调用返回时立即“切断”执行,而不是让事情处理掉?

我的回答将尝试回答上述具体问题,因为您已经得到了其他问题的答案。

在MVC框架中,有一个创建控制器的类。它名为DefaultControllerFactory,它有这个方法:

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

}

请注意上述方法所需的参数:IController。所以它传递你的控制器类(因为所有控制器通过派生BaseController来实现该接口),如果你的控制器实现IDisposable,它将转换它,然后在其上调用Dispose()。所以它让你有机会做你需要做的任何清理工作。

如果您已经推出了自己的工厂并且没有使用DefaultControllerFactory,那么请确保您正在执行相同操作并在控制器上调用Dispose

我从MVC's Source Code获得了上述源代码。

答案 2 :(得分:0)

不,当对象超出范围时,CLR将从不调用对象的Dispose。但是,它将调用类析构函数(必须明确重写)。

注意:下面的示例并非旨在演示生产代码中应该执行的操作。可以找到Microsoft推荐的做法here

考虑以下课程:

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

现在,编写使用它的方法,并在MyDispoable.Dispose方法上设置断点。

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

您将看到的是,离开此范围时,不仅Dispose MyDisposable未被调用,而且终结器也不会被调用。这意味着在方法结束时不会清除在方法中创建的本地,即使它们是方法的本地(不返回给调用者,也不分配给任何其他对象)。

我尝试在自己的代码中管理可支配资源的方式是不将它们用作类成员,并将它们全部放在using块中。请记住,使用只是try {...} finally {...}的语法糖。

现在,我认为你的问题更多的是关于ASP.NET而不是CLR。我不确定ASP.NET如何处理可支配资源。如果您正在使用Controller内部的资源,那么请继续实现IDisposable,并使用断点查看在方法完成后是否调用它。这是一个我不太确定的情景,但它可能会以这种方式运作似乎是合理的。

编辑:请参阅@CodingYoshi关于ASP.NET控制器处理的答案。事实证明,MVC将确定性地调用dispose,因此您的一些重构可能会稍微简单一些。