在密封的类上实现IDisposable

我不认为此问题曾经被问过。 我对在密封类上实现IDisposable的最佳方法感到困惑 – 特别是一个不从基类inheritance的密封类。 (也就是说,这是一个“纯密封的类”,这是我的术语。)

也许你们有些人同意我的看法,实施IDisposable的指导方针非常混乱。 也就是说,我想知道我打算实现IDisposable是足够和安全的。

我正在做一些通过Marshal.AllocHGlobal分配IntPtr P / Invoke代码,当然,我想干净地处理我创建的非托管内存。 所以我在考虑这样的事情

 using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] public sealed class MemBlock : IDisposable { IntPtr ptr; int length; MemBlock(int size) { ptr = Marshal.AllocHGlobal(size); length = size; } public void Dispose() { if (ptr != IntPtr.Zero) { Marshal.FreeHGlobal(ptr); ptr = IntPtr.Zero; GC.SuppressFinalize(this); } } ~MemBlock() { Dispose(); } } 

我假设因为MemBlock是完全密封的,并且永远不会从另一个类派生出来,因此不需要实现virtual protected Dispose(bool disposing)

那么,终结者是否必须? 欢迎所有的想法。

如果您忘记调用Dispose则终结器必须作为最终释放非托管资源的回退机制。

不,您不应该在sealed类中声明virtual方法。 根本不会编译。 此外,不建议在sealed类中声明新的protected成员。

一个小小的补充; 在一般情况下,一个常见的模式是使用Dispose(bool disposing)方法,这样你就知道你是否处于Dispose (有更多的东西可用)和终结器(你不应该真正接触任何其他连接的托管)对象)。

例如:

  public void Dispose() { Dispose(true); } ~MemBlock() { Dispose(false); } void Dispose(bool disposing) { // would be protected virtual if not sealed if(disposing) { // only run this logic when Dispose is called GC.SuppressFinalize(this); // and anything else that touches managed objects } if (ptr != IntPtr.Zero) { Marshal.FreeHGlobal(ptr); ptr = IntPtr.Zero; } } 

来自Joe Duffy的博客 :

对于密封类,不需要遵循这种模式,这意味着您应该使用简单的方法(即在C#中使用~T()(Finalize)和Dispose()来实现Finalizer和Dispose。 选择后一种方法时,您的代码仍应遵循以下关于最终化和处置逻辑的实现的指导原则。

所以,是的,你应该做得很好。

你确实需要像Mehrdad提到的终结器。 如果你想避免它,你可以看一下SafeHandle 。 我没有足够的P / Invoke经验来建议正确的用法。

您不能在密封类中声明虚方法。 同样在密封类中声明受保护的成员会给出编译器警告。 所以你已经正确实现了它。 从终结器中调用GC.SuppressFinalize(this)不是必需的,但是它不会有害。

在处理非托管资源时,拥有终结器是必不可少的,因为它们不会自动释放,您必须在终结器中执行它,并在对象被垃圾回收后自动调用。