If a class has开发者_运维知识库 a property which contains unmanaged resources. How to make sure no memory leak when using the class
Class A
{
B {get; set;}
}
B contains unmanaged resources.
Implement IDisposable
and clean up your unmanaged resources by calling Dispose()
, preferably placing the call to Dispose
in a finally
statement so you clean up resources even in the case of an exception.
C# has a using
keyword that you can employ to make sure that the Dispose
method is called, even if an exception is thrown.
EDIT: Incorporated call to GC.SuppressFinalize and finalizer implementation per Ran's answer
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
}
}
}
Using IDisposable
might not be enough, because it relies on the user remembering to call Dispose
or to use using
etc.
For a complete solution, combine IDisposable
and a finalizer. Like this:
Edit: Made some corrections in the Dispose method based on SpeksETC's comment.
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
...
}
}
This ensures that native resources will always be cleared, even if the user forgets to call Dispose
, while still allowing the user to clear the resources early.
The if
inside the Dispose
implementation is needed because if this class is being finalized, you may not call Dispose
on your members because they may have been GC'ed already.
I think it is important to point out that B is a managed resource that only contains unmanaged resources.
Therefore, A should implement IDisposable and dispose of B in its Dispose() method, but does not need a finalizer since it doesn't have any unmanaged resources to clean up - the finalizer needs to be implemented within B itself. Even if you implemented a finalizer it would call a Dispose that looked like this:
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 is correct in that if B contains unmanaged resources then B should ensure there are no leaks not A.
But what are the unmanaged resources and would the SafeHandle class help? Then B would contain a SafeHandle which would contain the unmanaged resource and you wouldn't need to worry about it.
精彩评论