在密封的类上实现IDisposable

时间:2009-09-13 22:05:05

标签: c# pinvoke

我认为以前没有问过这个问题。我对在密封类上实现IDisposable的最佳方法感到困惑 - 特别是一个不从基类继承的密封类。 (也就是说,这是一个“纯密封的类”,这是我的术语。)

也许你们中有些人同意我的观点,因为实施IDisposable的准则非常混乱。也就是说,我想知道我打算实施IDisposable的方式是充分和安全的。

我正在做一些分配IntPtrMarshal.AllocHGlobal的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)

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

4 个答案:

答案 0 :(得分:13)

如果忘记拨打Dispose,终结器必须作为最终释放非托管资源的后备机制。

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

答案 1 :(得分:8)

次要添加;在一般的情况下,常见的模式是使用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;
     }
 }

答案 2 :(得分:7)

来自Joe Duffy's Weblog

  

对于密封类,这种模式需要   不被遵循,这意味着你应该   只需实现你的终结器和   用简单的方法处理(即   ~T()(Finalize)和C#中的Dispose()。   选择后一种路线时,你的   代码应该仍然坚持   以下指南   最终确定和实施   处置逻辑。

所以是的,你应该很好。

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

答案 3 :(得分:1)

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

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