2017-04-14 79 views
0

我正在编写一个ASP.NET Web API。作为其中的一部分,我有一个课程,我在测试过程中发现需要实施IDisposable以确保管理资源的释放。C#在超出范围时自动“处置”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自动Dispose()对象,当他们掉下来的范围是什么?或者,我真的必须这样做...

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

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

我不知道ASP.NET在完成时如何处理请求 - 即框架是否给Dispose()一些代码时间,或者在调用return时立即“切断”执行,不让事物处理?

,如果没有实施IDisposable自己的MyObject类里面的东西都是未发行的资源因失败导致泄漏的事实,那感觉就像.NET确实自动Dispose事情。所以,如果是这样的话,我可以做些什么,所以我不需要重构数百种方法吗?


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

+2

实际上'处置'如果为释放**非托管**资源,而不是**管理**。这就是为什么你必须手动调用它。 –

+0

@ kirill不,这不是真的。每个实现'IDisposable'的类都是一个托管对象,并且仍然需要释放。也许它内部使用了非托管代码,但这是最重要的。 – CodingYoshi

+0

@CodingYoshi - > https://msdn.microsoft.com/en-us/library/fs2xkftw(v=vs.110).aspx –

回答

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)当对象被最终确定实现的方法,但你不能处分(甚至访问)管理对象(即:其他实施.Dispose()的东西)disposingfalse

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

有一个真正的,真的,由做了很好的工作,说明如何处置工作,以及如何正确地写自己的disposeable对象(例如斯蒂芬·克利里“IDisposable: What Your Mother Never Told You About Resource Deallocation”写一篇好文章,是需要注意的格局我提到以上是微软推荐的,但实际上是一种非常糟糕的模式。类应该只保存受管资源,或者从SafeHandle派生,并且只保存非托管资源,可能还包含其他SafeHandles。你永远不应该有一个拥有托管和非托管资源的类,也不能拥有一个拥有多个非托管资源的类)

+0

“你永远不应该拥有一个拥有托管和非托管资源的类)” - 我第一次听到这个。任何建议更精细的阅读? – CodingYoshi

+0

@CodingYoshi建议来自我链接到的文章,特别是题为“解决IDisposable的困难 - 通过利用一次性设计原则最大限度减少IDisposable使用案例”的部分 –

+0

谢谢@scott。将确保阅读它。 – CodingYoshi

0

不,CLR将从不调用在对象超出范围时处置。但是,它会调用类的析构函数(它必须被显式覆盖)。

注意:下面的示例不是为了演示生产代码应该做什么。微软推荐的做法可以在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"); 
} 

你将看到的是,离开这个范围时,不仅是MyDisposableDispose不叫,但也不是终结。这意味着您在方法中创建的本地程序在方法结束时不会被清理,即使它们在方法的本地(不返回给调用者或分配给任何其他对象)。

我试图在自己的代码中管理一次性资源的方式是不使用它们作为类成员,并将它们全部放入using块中。请记住,虽然使用try {...} finally {...}仅仅是语法糖。

现在,我认为你的问题是关于ASP.NET比CLR更多。我不确定ASP.NET是如何处理一次性资源的。如果您使用的是Controller内部的资源,那么请执行IDisposable,然后使用中断点查看在您的方法完成后它是否被调用。这是一个我并不确定的情景,但似乎可以这样工作。

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

+0

对于在析构函数方法中可以执行的操作有非常严格的限制,只需在其中调用Dispose()是一个非常不安全的建议。 –

+0

当然。这只是为了演示 - 不是为了真正的实现。重点是要表明,在C#中,资源不是根据更改范围内在处理/收集的。 – PSGuy

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得到了上述源代码。

+0

我怀疑这种行为可能是这种情况,并感谢您发布此特定内容。 – PSGuy