问题
(Note: this question is related to Calling GC.SuppressFinalize() from within a finalizer but is not a duplicate, as this question is explicitly specifically about the case of no managed resources, whereas that one is explicitly about when having both managed and unmanaged resources.)
The classic Disposable Pattern in C# goes roughly like this:
public class MyClass : IDisposable {
bool alreadyDisposed = false;
void Dispose(bool calledFromDisposeMethod) {
if (!alreadyDisposed) {
alreadyDisposed = true;
if (calledFromDisposeMethod) {
GC.SuppressFinalize(this);
// Release _managed_ resources here
}
// Release _unmanaged_ resources here
}
}
public void Dispose() => Dispose(true);
~MyClass() => Dispose(false);
}
The rationale is that when an instance of MyClass
is disposed of explicitly (by calling the Dispose()
method - possibly, but not necessarily, by using using
) then we want to release all resources, managed and unmanaged alike, whereas when the instance is garbage-collected without having already been disposed of then we only want to release unmanaged resources, leaving managed resources to be taken care of elsewhere (e.g. letting them be until they themselves are also garbage-collected).
By the way, note the call GC.SuppressFinalize(this)
, which tells the garbage collector there's no need to call the finalizer if and when the instance gets garbage-collected, as Dispose()
has already been called and taken care of releasing resources. Because of the use of the alreadyDisposed
flag there's no real danger if the finalizer does get called, but it's unnecessary, and letting the garbage collector know that allows it to skip putting the instance in the finalization queue, thus potentially freeing it (and other stuff it references) faster.
But now what about the case where we only have unmanaged resources? I would expect to find the following, somewhat simpler pattern:
public class MyClass : IDisposable {
bool alreadyDisposed = false;
void Dispose() {
if (!alreadyDisposed) {
alreadyDisposed = true;
// Release (unmanaged) resources here
GC.SuppressFinalize(this);
}
}
~MyClass() => Dispose();
}
Note once again the call GC.SuppressFinalize(this)
, which has the same function as above. In this case it might end up being called (indirectly) from the finalizer itself, but as far as I know there's no harm in that.
This form of the Disposable Pattern seems simpler, and is, as far as I can tell, perfectly adequate - when there are no managed resources involved. And yet, I haven't seen it anywhere, or at least I haven't seen it condoned anywhere.
Does this pattern make sense, or is it fundamentally flawed?
来源:https://stackoverflow.com/questions/56924439/can-the-c-sharp-disposable-pattern-be-simplified-when-only-unmanaged-resources