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

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

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

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 是完全密封的,并且永远不会从另一个类派生出来,因此不需要实现虚拟保护Dispose(bool disposing)

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

有帮助吗?

解决方案

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

不,您不应在 sealed 类中声明 virtual 方法。根本不会编译。此外,不建议在密封类中声明新的 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的博客

  

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

所以是的,你应该很好。

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top