在密封的类上实现IDisposable

zeb*_*box 24 c# pinvoke

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

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

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

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();
     }    
}
Run Code Online (Sandbox Code Playgroud)

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

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

Meh*_*ari 14

如果你忘了打电话,终结器作为最终释放非托管资源的后备机制是必要的Dispose.

不,你不应该virtualsealed类中声明一个方法.根本不会编译.此外,不建议protectedsealed类中声明新成员.

  • `GC.SuppressFinalize`阻止了最终化及其开销. (2认同)

Mar*_*ell 10

一个小小的补充; 在一般情况下,一个常见的模式是有一个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;
     }
 }
Run Code Online (Sandbox Code Playgroud)


Tru*_*ill 8

来自Joe Duffy的博客:

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

所以,是的,你应该很好.

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