通过阅读Microsoft文档 ,我知道IDisposable接口的“主要”用途是清理非托管资源。

对我来说,“非托管”意味着诸如数据库连接,套接字,窗口句柄之类的东西。但是,我已经看到了在其中实现Dispose()方法以释放托管资源的代码,这对我来说似乎是多余的,因为垃圾收集器应该照顾好你

例如:

public class MyCollection : IDisposable
{private List<String> _theList = new List<String>();private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();// Die, clear it up! (free unmanaged resources)public void Dispose(){_theList.clear();_theDict.clear();_theList = null;_theDict = null;}

我的问题是,这是否会使MyCollection使用的垃圾收集器释放内存的速度比正常情况更快?

编辑 :到目前为止,人们已经发布了一些使用IDisposable清除数据库,连接和位图等非托管资源的良好示例。 但是假设_theList在上面的代码中包含上百万字符串,你想现在就释放内存,而不是等待垃圾回收器。 上面的代码能做到吗?


#1楼

如果要立即删除 ,请使用非托管内存

看到:

  • 全球法警
  • 自由元帅
  • 元帅破坏结构

#2楼

除了其主要用途是控制系统资源 生存期的一种方法(完全由Ian的出色回答,功劳!)之外, IDisposable / using组合还可以用于确定(关键)全局资源的状态变化控制台线程进程 ,任何全局对象(应用程序实例)

我已经写了一篇关于这种模式的文章: http : //pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/

它说明了如何以可重用可读的方式保护某些经常使用的全局状态: 控制台颜色 ,当前线程区域性Excel应用程序对象属性 ...


#3楼

首先定义。 对我来说,非托管资源意味着某些类,该类实现IDisposable接口或使用对dll的调用创建的某些类。 GC不知道如何处理此类对象。 如果类仅具有例如值类型,那么我不认为此类是具有非托管资源的类。 对于我的代码,我遵循以下做法:

  1. 如果由我创建的类使用一些非托管资源,则意味着我还应该实现IDisposable接口以清理内存。
  2. 用完后立即清理对象。
  3. 在我的dispose方法中,我遍历该类的所有IDisposable成员,然后调用Dispose。
  4. 在我的Dispose方法中,调用GC.SuppressFinalize(this)以便通知垃圾收集器我的对象已被清理。 我这样做是因为调用GC是一项昂贵的操作。
  5. 作为其他预防措施,我尝试使多次调用Dispose()成为可能。
  6. 有时我添加了私有成员_disposed,并在方法调用中检查了对象是否被清除。 如果已清除,则生成ObjectDisposedException
    以下模板演示了我用文字描述的作为代码示例:
public class SomeClass : IDisposable{/// <summary>/// As usually I don't care was object disposed or not/// </summary>public void SomeMethod(){if (_disposed)throw new ObjectDisposedException("SomeClass instance been disposed");}public void Dispose(){Dispose(true);}private bool _disposed;protected virtual void Dispose(bool disposing){if (_disposed)return;if (disposing)//we are in the first call{}_disposed = true;}}

#4楼

处置托管资源最合理的用例是为GC回收可能永远不会收集的资源做准备。

一个主要的例子是循环引用。

最佳做法是使用避免循环引用的模式,但是,如果您最终得到(例如)引用返回其“父级”的“子级”对象,那么如果您放弃了,则会停止父级的GC收集引用并依赖GC-再加上如果您实现了终结器,则永远不会调用它。

解决此问题的唯一方法是通过将子代的“父代”引用设置为null来手动中断循环引用。

在父母和孩子上实现IDisposable是最好的方法。 当对父对象调用Dispose时,请对所有子对象调用Dispose,然后在子对象Dispose方法中,将父对象引用设置为null。


#5楼

给定的代码示例不是IDisposable用法的好例子。 通常,清除字典不应该使用Dispose方法。 超出范围时,词典项目将被清除并处置。 需要IDisposable实现来释放一些内存/处理程序,即使它们超出范围也不会释放/释放。

以下示例显示了IDisposable模式的良好示例,其中包含一些代码和注释。

public class DisposeExample
{// A base class that implements IDisposable. // By implementing IDisposable, you are announcing that // instances of this type allocate scarce resources. public class MyResource: IDisposable{// Pointer to an external unmanaged resource. private IntPtr handle;// Other managed resource this class uses. private Component component = new Component();// Track whether Dispose has been called. private bool disposed = false;// The class constructor. public MyResource(IntPtr handle){this.handle = handle;}// Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. public void Dispose(){Dispose(true);// This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time.GC.SuppressFinalize(this);}// Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user's code. Managed and unmanaged resources // can be disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be disposed. protected virtual void Dispose(bool disposing){// Check to see if Dispose has already been called. if(!this.disposed){// If disposing equals true, dispose all managed // and unmanaged resources. if(disposing){// Dispose managed resources.component.Dispose();}// Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed.CloseHandle(handle);handle = IntPtr.Zero;// Note disposing has been done.disposed = true;}}// Use interop to call the method necessary // to clean up the unmanaged resource.[System.Runtime.InteropServices.DllImport("Kernel32")]private extern static Boolean CloseHandle(IntPtr handle);// Use C# destructor syntax for finalization code. // This destructor will run only if the Dispose method // does not get called. // It gives your base class the opportunity to finalize. // Do not provide destructors in types derived from this class.~MyResource(){// Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability.Dispose(false);}}public static void Main(){// Insert code here to create // and use the MyResource object.}
}

#6楼

我看到很多答案已经转移到谈论将IDisposable用于托管和非托管资源。 我建议将本文作为对IDisposable应如何实际使用的最佳解释之一。

https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

对于实际问题; 如果您使用IDisposable清理占用大量内存的托管对象,则简短的回答是“ 否” 。 原因是,一旦处置IDisposable,就应该让它超出范围。 届时,所有引用的子对象也将超出范围,并将被收集。

唯一真正的例外是,如果您在托管对象中占用了大量内存,并且阻塞了该线程,等待某些操作完成。 如果在该调用完成后不需要使用那些对象,则将这些引用设置为null可能会使垃圾收集器更快地收集它们。 但是这种情况将代表需要重构的错误代码,而不是IDisposable的用例。


#7楼

是的,该代码是完全冗余且不必要的,并且不会使垃圾收集器执行其他操作(如果MyCollection的实例超出范围,则是.Clear() 。尤其是.Clear()调用。

回答您的编辑:有点。 如果我这样做:

public void WasteMemory()
{var instance = new MyCollection(); // this one has no Dispose() methodinstance.FillItWithAMillionStrings();
}// 1 million strings are in memory, but marked for reclamation by the GC

就内存管理而言,它在功能上是相同的:

public void WasteMemory()
{var instance = new MyCollection(); // this one has your Dispose()instance.FillItWithAMillionStrings();instance.Dispose();
}// 1 million strings are in memory, but marked for reclamation by the GC

如果确实真的需要立即释放内存,请调用GC.Collect() 。 不过,这里没有理由这样做。 需要时将释放内存。


#8楼

IDisposable通常用于利用using语句,并利用一种简单的方法来确定性地清除托管对象。

public class LoggingContext : IDisposable {public Finicky(string name) {Log.Write("Entering Log Context {0}", name);Log.Indent();}public void Dispose() {Log.Outdent();}public static void Main() {Log.Write("Some initial stuff.");try {using(new LoggingContext()) {Log.Write("Some stuff inside the context.");throw new Exception();}} catch {Log.Write("Man, that was a heavy exception caught from inside a child logging context!");} finally {Log.Write("Some final stuff.");}}
}

#9楼

如果MyCollection都要对MyCollection进行垃圾回收,那么您就不需要处理它。 这样做只会使CPU不必要地运转,甚至可能使垃圾收集器已经执行的某些预先计算的分析无效。

我使用IDisposable进行诸如确保正确处理线程以及非托管资源之类的事情。

编辑回应斯科特的评论:

只有当调用[sic] GC.Collect()时,GC性能指标才会受到影响”

从概念上讲,GC维护对象引用图的视图以及线程堆栈框架对其的所有引用。 该堆可能很大,并且跨越了许多页的内存。 作为一种优化,GC会缓存其对不太可能经常更改的页面的分析,以避免不必要地重新扫描该页面。 当页面中的数据发生更改时,GC会从内核接收通知,因此它知道该页面已脏,需要重新扫描。 如果集合在Gen0中,则页面中的其他内容也可能会发生变化,但是在Gen1和Gen2中的可能性较小。 有趣的是,对于将GC移植到Mac上以使Silverlight插件在该平台上运行的团队而言,这些挂钩在Mac OS X中不可用。

避免不必要地浪费资源的另一点是:想象一个过程正在卸载的情况。 还可以想象该进程已经运行了一段时间。 很有可能该进程的许多内存页面已被交换到磁盘。 至少它们不再位于L1或L2缓存中。 在这种情况下,卸载的应用程序将所有这些数据和代码页交换回内存以释放“释放”资源是没有意义的,而这些资源将在进程终止时由操作系统释放。 这适用于托管甚至某些非托管资源。 仅必须保留使非后台线程保持活动状态的资源,否则该进程将保持活动状态。

现在,在正常执行过程中,必须正确清除临时资源(如@fezmonkey指出数据库连接,套接字,窗口句柄 ),以避免非托管内存泄漏。 这些都是必须处理的事情。 如果您创建一个拥有线程的类(并拥有一个,我的意思是它创建了它,因此至少是按照我的编码风格负责确保它停止),那么该类很可能必须实现IDisposable并在执行过程中拆除该线程Dispose

.NET框架使用IDisposable接口作为信号,甚至警告开发人员必须丢弃此类。 我想不出框架中实现IDisposable的任何类型(不包括显式接口实现),其中处置是可选的。


#10楼

Dispose模式的目的是提供一种清除托管资源和非托管资源的机制,何时发生取决于如何调用Dispose方法。 在您的示例中,使用Dispose实际上并没有执行任何与Dispose相关的操作,因为清除列表不会对该被处置的集合产生影响。 同样,将变量设置为null的调用也不会影响GC。

您可以看一下这篇文章,以获取有关如何实现Dispose模式的更多详细信息,但是它基本上看起来像这样:

public class SimpleCleanup : IDisposable
{// some fields that require cleanupprivate SafeHandle handle;private bool disposed = false; // to detect redundant callspublic SimpleCleanup(){this.handle = /*...*/;}protected virtual void Dispose(bool disposing){if (!disposed){if (disposing){// Dispose managed resources.if (handle != null){handle.Dispose();}}// Dispose unmanaged managed resources.disposed = true;}}public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}
}

这里最重要的方法是Dispose(bool),它实际上在两种不同的情况下运行:

  • dispose == true:该方法已由用户代码直接或间接调用。 可以处理托管和非托管资源。
  • dispose == false:运行时已从终结器内部调用了该方法,因此您不应引用其他对象。 只能处置非托管资源。

简单地让GC负责清理的问题是,您无法真正控制GC何时运行收集周期(可以调用GC.Collect(),但实际上不应该这样做),因此资源可能会保留下来大约比需要的时间更长。 请记住,调用Dispose()实际上并不会导致收集周期或以任何方式导致GC收集/释放对象。 它只是提供了一种方法,可以更确定地清除所使用的资源,并告诉GC已经执行了该清除操作。

IDisposable和dispose模式的全部要点不是立即释放内存。 实际上,对Dispose的调用实际上甚至唯一有机会立即释放内存的时间是在处理Dispose == false方案并处理非托管资源时。 对于托管代码,在GC运行收集周期之前实际上不会回收内存,您实际上无法对其进行控制(除了调用GC.Collect(),我已经提到过这不是一个好主意)。

您的方案不是真的有效,因为.NET中的字符串不使用任何未使用的资源,也不实现IDisposable,因此无法强制对其进行“清理”。


#11楼

在示例代码中, Dispose()操作所做的某些事情可能会由于MyCollection对象的正常GC而不会产生效果。

如果_theList_theDict引用的对象被其他对象引用,则该List<>Dictionary<>对象将不受收集,但突然不包含任何内容。 如果没有如示例中的Dispose()操作,则这些集合仍将包含其内容。

当然,如果是这种情况,我会称其为残破的设计-我只是指出(我想是做个Dispose()操作可能不是完全多余的,这取决于List<>是否还有其他用途。片段中未显示的List<>Dictionary<>


#12楼

处置的重点释放非托管资源。 它需要在某个时候完成,否则它们将永远不会被清除。 垃圾收集器不知道如何IntPtr类型的变量上调用DeleteHandle() ,也不知道是否需要调用DeleteHandle()

注意 :什么是非托管资源 ? 如果您在Microsoft .NET Framework中找到它:它是受管理的。 如果您自己去逛MSDN,那它是不受管理的。 您使用P / Invoke调用进入.NET Framework中所有可用内容的舒适环境都是不受管理的-现在您要负责清理它。

您创建的对象需要公开一些外界可以调用的方法,以清理非托管资源。 该方法可以命名为任意名称:

public void Cleanup()

要么

public void Shutdown()

但是,此方法有一个标准化名称:

public void Dispose()

甚至创建了一个接口IDisposable ,它只有一个方法:

public interface IDisposable
{void Dispose()
}

因此,使您的对象公开IDisposable接口,并以此方式保证已编写该单一方法来清理非托管资源:

public void Dispose()
{Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}

这样就完成了。 除了可以做得更好。


如果您的对象已将250MB System.Drawing.Bitmap (即.NET托管的Bitmap类)分配为某种帧缓冲区怎么办? 当然,这是一个托管的.NET对象,垃圾收集器将释放它。 但是,您是否真的要保留250MB的内存,等待垃圾收集器最终释放出来并释放它呢? 如果有开放的数据库连接怎么办? 当然,我们不希望该连接处于打开状态,而是等待GC最终确定对象。

如果用户调用了Dispose() (意味着他们不再打算使用该对象),为什么不摆脱那些浪费的位图和数据库连接呢?

所以现在我们将:

  • 摆脱非托管资源(因为我们必须这样做),并且
  • 摆脱托管资源(因为我们希望有所帮助)

因此,让我们更新Dispose()方法来摆脱那些托管对象:

public void Dispose()
{//Free unmanaged resourcesWin32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);//Free managed resources tooif (this.databaseConnection != null){this.databaseConnection.Dispose();this.databaseConnection = null;}if (this.frameBufferImage != null){this.frameBufferImage.Dispose();this.frameBufferImage = null;}
}

一切都很好, 但您可以做得更好


如果该人忘记在您的对象上调用Dispose()怎么办? 然后他们将泄漏一些不受管理的资源!

注意:它们不会泄漏托管资源,因为最终垃圾收集器将在后台线程上运行,并释放与任何未使用的对象关联的内存。 这将包括您的对象以及您使用的任何托管对象(例如BitmapDbConnection )。

如果该人忘记了调用Dispose() ,我们仍然可以保存他们的培根! 我们仍然可以它们调用此方法:当垃圾收集器最终开始释放(即完成)对象时。

注意:垃圾收集器最终将释放所有托管对象。 完成后,它将在对象上调用Finalize方法。 GC不了解或不在乎您的 Dispose方法。 当我们想要摆脱不受管理的东西时,这只是我们为调用的方法选择的名称。

垃圾收集器销毁我们的对象是释放那些烦人的非托管资源的绝佳时机。 我们通过重写Finalize()方法来做到这一点。

注意:在C#中,您不会显式覆盖Finalize()方法。 您编写了一个看起来像 C ++析构函数的方法 ,并且编译器将其用作Finalize()方法的实现:

~MyObject()
{//we're being finalized (i.e. destroyed), call Dispose in case the user forgot toDispose(); //<--Warning: subtle bug! Keep reading!
}

但是该代码中存在一个错误。 您会看到,垃圾收集器在后台线程上运行; 您不知道销毁两个对象的顺序。 很有可能在您的Dispose()代码中,您试图摆脱的托管对象(因为您想提供帮助)不再存在:

public void Dispose()
{//Free unmanaged resourcesWin32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);//Free managed resources tooif (this.databaseConnection != null){this.databaseConnection.Dispose(); //<-- crash, GC already destroyed itthis.databaseConnection = null;}if (this.frameBufferImage != null){this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed itthis.frameBufferImage = null;}
}

因此,您需要的是Finalize()告诉Dispose()一种方式,它应该不接触任何托管资源(因为它们可能不再存在 ),同时仍释放非托管资源。

执行此操作的标准模式是让Finalize()Dispose()都调用第三个 (!)方法。 在此处传递一个布尔值,表示是否要从Dispose()调用它(而不是Finalize() ),这意味着释放托管资源是安全的。

这个内部方法可以给出像“CoreDispose”,或“MyInternalDispose”一些任意名称,但传统调用它Dispose(Boolean)

protected void Dispose(Boolean disposing)

但是更有用的参数名称可能是:

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{//Free unmanaged resourcesWin32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);//Free managed resources too, but only if I'm being called from Dispose//(If I'm being called from Finalize then the objects might not exist//anymoreif (itIsSafeToAlsoFreeManagedObjects)  {    if (this.databaseConnection != null){this.databaseConnection.Dispose();this.databaseConnection = null;}if (this.frameBufferImage != null){this.frameBufferImage.Dispose();this.frameBufferImage = null;}}
}

然后,将IDisposable.Dispose()方法的实现更改为:

public void Dispose()
{Dispose(true); //I am calling you from Dispose, it's safe
}

和您的终结者可以:

~MyObject()
{Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}

注意 :如果您的对象从实现Dispose的对象Dispose ,那么在覆盖Dispose时,请不要忘记调用其基本 Dispose方法:

public override void Dispose()
{try{Dispose(true); //true: safe to free managed resources}finally{base.Dispose();}
}

一切都很好, 但您可以做得更好


如果用户在您的对象上调用Dispose() ,则所有内容均已清除。 稍后,当垃圾收集器出现并调用Finalize时,它将再次调用Dispose

这不仅浪费,而且如果您的对象具有对您从上次Dispose()调用以来已Dispose()的对象的垃圾引用,您将尝试再次废弃它们!

您会在代码中注意到,我小心删除了对已处置对象的引用,因此,我不会尝试在垃圾对象引用上调用Dispose 。 但这并不能阻止潜伏的小错误。

当用户调用Dispose() ,句柄CursorFileBitmapIconServiceHandle被破坏。 稍后当垃圾收集器运行时,它将尝试再次破坏相同的句柄。

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{//Free unmanaged resourcesWin32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy ...
}

解决此问题的方法是告诉垃圾收集器,它无需费心地完成对象的处理-它的资源已被清理,并且不再需要任何工作。 您可以通过在Dispose()方法中调用GC.SuppressFinalize()来做到这一点:

public void Dispose()
{Dispose(true); //I am calling you from Dispose, it's safeGC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}

现在,用户已调用Dispose() ,我们有了:

  • 释放非托管资源
  • 释放托管资源

GC运行终结器没有任何意义-一切都已处理完毕。

我不能使用Finalize清理非托管资源吗?

Object.Finalize的文档说:

Finalize方法用于在销毁对象之前对当前对象所拥有的非托管资源执行清理操作。

但是MSDN文档也对IDisposable.Dispose说:

执行与释放,释放或重置非托管资源相关的应用程序定义的任务。

那是什么呢? 我可以清理哪一个托管资源? 答案是:

这是你的选择! 但是选择“ Dispose

您当然可以将未管理的清除操作放入终结器中:

~MyObject()
{//Free unmanaged resourcesWin32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);//A C# destructor automatically calls the destructor of its base class.
}

这样做的问题是您不知道何时垃圾回收器会完成您的对象。 您的未管理,不需要,未使用的本机资源将一直存在,直到垃圾收集器最终运行为止。 然后它将调用您的finalizer方法; 清理非托管资源。 Object.Finalize的文档指出了这一点:

终结器执行的确切时间是不确定的。 为确保确定释放类实例的资源,请实现Close方法或提供IDisposable.Dispose实现。

这是使用Dispose清理非托管资源的优点。 您将了解并控制何时清理非托管资源。 他们的破坏是“确定性的”


要回答您最初的问题:为什么现在不释放内存,而不是在GC决定这样做时释放内存? 我有一个面部识别软件,由于不再需要它们, 现在 需要摆脱530 MB的内部图像。 当我们不这样做时:机器会陷入停顿状态。

奖励阅读

对于喜欢此答案样式的人(解释为什么 ,因此如何变得显而易见),建议您阅读Don Box的Essential COM的第一章:

  • 直接链接: Pearson Publishing的第1章样本
  • 磁铁:84bf0b960936d677190a2be355858e80ef7542c0

在35页中,他解释了使用二进制对象的问题,并在您的面前发明了COM。 一旦了解了COM的原因 ,剩下的300页就显而易见了,只详细介绍了Microsoft的实现。

我认为每个曾经处理过对象或COM的程序员都应该至少阅读第一章。 这是有史以来最好的解释。

额外奖金阅读

当您知道的一切都不对时 ,埃里克·利珀特(Eric Lippert)

因此,确实很难编写正确的终结器,而我能给您的最佳建议是不要尝试


#13楼

我使用IDisposable的方案:清理非托管资源,取消订阅事件,关闭连接

我用于实现IDisposable( 不是threadsafe )的惯用法:

class MyClass : IDisposable {// ...#region IDisposable Members and Helpersprivate bool disposed = false;public void Dispose() {Dispose(true);GC.SuppressFinalize(this);}private void Dispose(bool disposing) {if (!this.disposed) {if (disposing) {// cleanup code goes here}disposed = true;}}~MyClass() {Dispose(false);}#endregion
}

#14楼

调用Dispose之后,不应再对对象的方法进行任何调用(尽管对象应允许对Dispose的进一步调用)。 因此,问题中的例子很愚蠢。 如果调用了Dispose,则对象本身可以被丢弃。 因此,用户应该只丢弃对整个对象的所有引用(将它们设置为null),并且它内部的所有相关对象都将自动清除。

至于有关托管/非托管的一般性问题以及其他答案中的讨论,我认为对此问题的任何回答都必须以非托管资源的定义开始。

归结为是,可以调用一个函数使系统进入状态,而可以调用另一个函数使系统退出该状态。 现在,在典型示例中,第一个可能是返回文件句柄的函数,第二个可能是对CloseHandle的调用。

但是-这是关键-它们可以是任何匹配的功能对。 一个建立一个状态,另一个建立状态。 如果状态已建立但尚未拆除,则该资源的实例存在。 您必须安排在正确的时间进行拆卸-资源不是由CLR管理的。 唯一自动管理的资源类型是内存。 有两种:GC和堆栈。 值类型由堆栈(或通过引用类型内的游标)管理,而引用类型由GC管理。

这些功能可能会导致状态更改,可以自由交错,或者可能需要完美嵌套。 状态更改可能是线程安全的,也可能不是。

看正义问题中的例子。 对日志文件的缩进的更改必须完美嵌套,否则一切都会出错。 而且它们不太可能是线程安全的。

可以与垃圾收集器搭便车,以清理您的非托管资源。 但是,仅当状态更改函数是线程安全的并且两个状态可以具有以任何方式重叠的生存期时,才可以。 因此,Justice的资源示例不能包含终结器! 它只是无济于事。

对于这些资源,您可以仅实现IDisposable ,而无需使用终结器。 终结器绝对是可选的-必须如此。 这在很多书中都被掩盖甚至没有提到。

然后,您必须使用using语句才能确保调用了Dispose 。 这基本上是像钩连一程与所述堆叠(从而终结是到GC, using是在堆栈)。

缺少的部分是您必须手动编写Dispose并使其调用您的字段和基类。 C ++ / CLI程序员不必这样做。 在大多数情况下,编译器会为他们编写代码。

有一种选择,对于状态嵌套完美且不是线程安全的状态,我更喜欢(除了其他任何事情,避免使用IDisposable可以避免与无法拒绝向实现IDisposable的每个类添加终结器的人发生争执的问题) 。

不用编写类,而是编写一个函数。 该函数接受委托以回调:

public static void Indented(this Log log, Action action)
{log.Indent();try{action();}finally{log.Outdent();}
}

然后一个简单的例子是:

Log.Write("Message at the top");
Log.Indented(() =>
{Log.Write("And this is indented");Log.Indented(() =>{Log.Write("This is even more indented");});
});
Log.Write("Back at the outermost level again");

拉姆达传递中作为一个代码块,所以这就像在你自己手里控制结构来达到相同的目的是using ,除非你不再有来电者滥用它的危险。 他们没有办法清除资源。

如果资源是可能具有重叠生命周期的资源,则此技术的用处不大,因为那样的话,您希望能够先构建资源A,然后构建资源B,然后杀死资源A,然后再杀死资源B。您不能这样做如果您已强迫用户像这样完美嵌套。 但是,然后您需要使用IDisposable (但仍然没有终结器,除非您实现了线程安全,这不是免费的)。


#15楼

如果有什么事情,我期望的代码要离开它时相比,效率较低

不需要调用Clear()方法,如果Dispose不这样做,GC可能不会这样做。


#16楼

在您发布的示例中,它仍然没有“立即释放内存”。 所有内存都是垃圾回收的,但可以允许在较早的一代中回收内存。 您必须运行一些测试才能确定。


框架设计准则是准则,而不是规则。 他们告诉您接口的主要用途,何时使用,如何使用以及何时不使用。

我曾经阅读过使用IDisposable失败时的简单RollBack()代码。 下面的MiniTx类将检查Dispose()上的标志,如果Commit调用从未发生,它将对其自身调用Rollback 。 它增加了一个间接层,使调用代码更容易理解和维护。 结果看起来像:

using( MiniTx tx = new MiniTx() )
{// code that might not work.tx.Commit();
}

我还看到定时/日志记录代码执行相同的操作。 在这种情况下,Dispose()方法停止计时器并记录该块已退出。

using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{// code to time...
}

因此,这里有几个具体示例,它们不执行任何非托管资源清除,但是确实成功使用IDisposable创建了更干净的代码。


#17楼

我将不再重复有关使用或释放​​非托管资源的常规内容,所有内容已涵盖。 但我想指出似乎是一个普遍的误解。
给出以下代码

Public Class LargeStuffImplements IDisposablePrivate _Large as string()'Some strange code that means _Large now contains several million long strings.Public Sub Dispose() Implements IDisposable.Dispose_Large=NothingEnd Sub

我认识到Disposable实现不遵循当前的准则,但是希望大家都明白。
现在,当调用Dispose时,释放了多少内存?

答:没有。
调用Dispose可以释放非托管资源,它不能回收托管内存,只有GC可以这样做。 并不是说上述不是一个好主意,遵循上述模式实际上仍然是一个好主意。 一旦运行Dispose,即使LargeStuff实例可能仍在范围内,也不会停止GC回收_Large所使用的内存。 _Large中的字符串也可能是第0代,但是LargeStuff的实例可能是第2代,因此,再次声明,将更快地回收内存。
尽管没有必要添加终结器来调用上面显示的Dispose方法。 那只会延迟对内存的回收,以允许终结器运行。


#18楼

IDisposable非常适合取消订阅事件。


#19楼

大多数有关“非托管资源”的讨论的一个问题是,它们实际上并未定义该术语,但似乎暗示它与非托管代码有关。 虽然确实有许多类型的非托管资源都可以与非托管代码交互,但是用这种术语来思考非托管资源并没有帮助。

相反,应该认识到所有托管资源的共同点:它们都包含一个对象,要求某个外部“事物”代表它做某事,从而损害其他“事物”,而另一个实体同意这样做,直到另行通知。 如果物体被遗弃并消失得无影无踪,那么没有任何东西可以告诉外界“事物”,它不再需要代表不存在的物体改变其行为。 因此,“事物”的用处将永久减少。

因此,非托管资源表示某个外部“事物”表示同意更改其代表某个对象的行为的协议,如果该对象被废弃并不再存在,则将无用地削弱该外部“事物”的用途。 受管资源是该协议的受益人,但已签名接收该对象的通知(如果该资源被放弃),并且将使用该通知在事务销毁之前将其事务整理。

正确使用IDisposable接口相关推荐

  1. .NET中IDisposable接口的基本使用

    首先来看MSDN中关于这个接口的说明: [ComVisible(true)] public interface IDisposable { // Methods void Dispose(); } 1 ...

  2. .NET 内存管理与垃圾回收:实现IDisposable接口和析构函数

    .NET 类所使用的释放未托管资源的两种方式: 1. 利用析构函数,此方法有很多问题 2. 实现IDisposable接口,但需要确保执行Dispose()方法 最好的情况是执行这两种机制,获得这两种 ...

  3. IDisposable 接口介绍

    定义一种释放分配的非托管资源的方法. 实现 IDisposable 的类 类 说明 AsymmetricAlgorithm 表示所有不对称算法的实现都必须从中继承的抽象基类. BinaryReader ...

  4. 利用IDisposable接口构建包含非托管资源对象

    托管资源与非托管资源 在.net中,对象使用的资源分为两种:托管资源与非托管资源.托管资源由CLR进行管理,不需要开发人员去人工进行控制,.NET中托管资源主要指"对象在堆中的内存" ...

  5. 如何编写提供了IDisposable接口的类.

    这里提供了一个代码模板,直接套用就行. public class BaseClass: IDisposable {        //实现 IDisposable 接口的Dispose()方法.    ...

  6. Nodejs实现微信网页授权及正确配置JS-SDK接口

    原文链接:<Nodejs实现微信网页授权及正确配置JS-SDK接口>- 陈帅华 帅华君将在本文介绍基于Nodejs实现微信网页授权以及如何正确在前后端配置JS-SDK接口. 微信网页授权 ...

  7. 怎样正确理解LOOPBACK接口?

    loopback具体作用是什么?怎么用? 此类接口是应用最为广泛的一种虚接口,几乎在每台路由器上都会使用.常见于如下用途. 作为一台路由器的管理地址 系统管理员完成网络规划之后,为了方便管理,会为每一 ...

  8. 如何正确规范写接口文档

    前言 正规的团队合作或者是项目对接,接口文档是非常重要的,一般接口文档都是通过开发人员写的.一个工整的文档显得是非重要.下面我将我看到的一篇接口文档做一个总结 开始吧!!! 接口1: 查询排重接口 接 ...

  9. 物以类聚:对象也有生命

    相关文章链接 编程之基础:数据类型(一) 编程之基础:数据类型(二) 高屋建瓴:梳理编程约定 动力之源:代码中的泵 难免的尴尬:代码依赖 重中之重:委托与事件 可复用代码:组件的来龙去脉 物以类聚:对 ...

最新文章

  1. ARM多寄存器加载/存储指令
  2. 【渝粤教育】国家开放大学2018年秋季 0727-21T思想道德修养与法律基础 参考试题
  3. 原生ajax通过post或者get下载文件,或处理IE浏览器因为后缀不能显示图片
  4. ICQ被购后看腾讯出击DST:迈出国际化的关键一步
  5. 今日恐慌与贪婪指数为38 恐慌程度明显上升
  6. 国产操作系统往事:四十年激变,终再起风云
  7. Java web ch02_3
  8. 河南理工大学计算机科学与技术怎么样,河南理工大学计算机科学与技术怎么样...
  9. python 教程 w3 school_Python 模块 | w3cschool菜鸟教程
  10. 鲨鱼游戏/游戏测试实习面试
  11. upc 卡德加的兔子 线段树 + 矩阵快速幂
  12. AutoVue 21.0.1新版本特性:支持在没有Applets的浏览器中运行
  13. 对话行癫:CTO 最重要的是判断未来!| 人物志
  14. Windows设置本地DNS域名解析Hosts文件的方法
  15. python语言打小数点_如何在python中打小数点-问答-阿里云开发者社区-阿里云
  16. 基于Rsoft的三芯光子晶体光纤数值仿真
  17. Linux基础命令---mysqladmin数据库管理工具
  18. python+pytest接口自动化-接口测试基础,http协议基础(一)
  19. activitygroup activity 启动时白屏、黑屏
  20. Mybatis中<trim>标签用法

热门文章

  1. 6425C-Lab3 管理用户与服务帐户(1)
  2. OOJ-面向对象编程的三大特点-封装,继承,多态分析与实例
  3. 2008年全国计算机等级考试须知及参考资料
  4. Linux对变量的截取替换
  5. Python接口测试之moco
  6. MySql(16)——Spring data jpa mysql 乐观锁 与 AtomicInteger
  7. [android]加载大量图片避免OOM
  8. 让Cookies操作变得向Session一样简单
  9. Zabbix email 配置
  10. MySQL 5.7 多实例单配置 多实例多配置文件安装 | 资料