我认为以前没有问过这个问题。我对在密封类上实现IDisposable
的最佳方法感到困惑 - 特别是一个不从基类继承的密封类。 (也就是说,这是一个“纯密封的类”,这是我的术语。)
也许你们中有些人同意我的观点,因为实施IDisposable
的准则非常混乱。也就是说,我想知道我打算实施IDisposable
的方式是充分和安全的。
我正在做一些分配IntPtr
到Marshal.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)
。
此外,终结者是否必须?欢迎所有的想法。
答案 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)
对于密封类,这种模式需要 不被遵循,这意味着你应该 只需实现你的终结器和 用简单的方法处理(即 ~T()(Finalize)和C#中的Dispose()。 选择后一种路线时,你的 代码应该仍然坚持 以下指南 最终确定和实施 处置逻辑。
所以是的,你应该很好。
你确实需要像Mehrdad所提到的终结者。如果你想避免它,你可以看看SafeHandle。我没有足够的P / Invoke经验来建议正确使用。
答案 3 :(得分:1)
您无法在密封类中声明虚方法。同样在密封类中声明受保护的成员会给出编译器警告。所以你已经正确实现了它。 从终结器中调用GC.SuppressFinalize(this)不是必要的,但是它不会有害。但
在处理非托管资源时,有一个终结器是必不可少的,因为它们不会自动释放,你必须在终结器中执行它,并在对象被垃圾回收后自动调用。