如果一个类有一个包含非托管资源的属性,如何确保在使用该类时不会出现内存泄漏。
Class A
{
B {get; set;}
}
B 包含未经管理的资源。
实现 IDisposable
接口并通过调用 Dispose()
方法清理您的非托管资源,最好将对 Dispose
的调用置于 finally
语句中,以便在发生异常时也能清理资源。
C# 中有一个 using
关键字,您可以使用它来确保调用 Dispose
方法,即使抛出异常也是如此。
编辑:根据 Ran 的答案添加了调用 GC.SuppressFinalize 和终结器实现。
class A : IDisposable
{
private bool _disposed;
~A()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// dispose managed resources
}
// clean up unmanaged resources
_disposed = true;
}
}
}
class Program
{
static void Main(string[] args)
{
using (var someInstance = new A())
{
// do some things with the class.
// once the using block completes, Dispose
// someInstance.Dispose() will automatically
// be called
}
}
}
仅使用 IDisposable
可能不够,因为它依赖于用户记得调用 Dispose
或使用 using
等。
要完整解决问题,可以将 IDisposable
和终结器结合使用。像这样:
编辑:根据 SpeksETC 的评论,在 Dispose 方法中进行了一些更正。
class MyClass : IDisposable
{
~MyClass()
{
Dispose(false);
}
public void Dispose()
{
GC.SupressFinalize();
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing)
{
// clear unmanaged resources here (can only be called once)
...
}
// dispose called explicitly by the user, clean up managed resources here
...
}
}
Dispose
,同时仍允许用户提前清除资源。Dispose
实现中的if
语句是必需的,因为如果这个类正在被终结,你可能无法调用Dispose
来处理成员,因为它们可能已经被垃圾回收。我认为需要指出的是,B是一个托管资源,仅包含非托管资源。
因此,在其Dispose()方法中,A应该实现IDisposable并处理B,但不需要终结器,因为它没有任何需要清理的非托管资源 - 终结器需要在B本身中实现。即使您实现了终结器,它也会调用类似于以下内容的Dispose:
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// dispose called explicitly by the user, clean up managed resources here
b.Dispose()
}
// no unmanaged resources to clean up so do nothing which makes Finalizer unneccesary
...
}
SpeksEtc的观点是正确的,如果B包含非托管资源,则B应确保没有泄漏,而不是A。
但是什么是非托管资源?SafeHandle类是否有帮助?然后B将包含一个SafeHandle,其中包含非托管资源,您就不必担心它了。