-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should all non-sealed classes implementing IDisposable call GC.SuppressFinalize even if they don't define a finalizer? #42183
Comments
Tagging subscribers to this area: @dotnet/gc |
cc @dotnet/fxdc |
Yes, they should; it's the rules of the Basic Dispose Pattern 😄. |
I wonder if we could have an analyzer validate this pattern. |
CA1816 seems to check that there is a SuppressFinalize call in But it would at least find all the places that we didn't do it at all. |
What is a breaking potential of adding the SuppressFinalize calls to existing types that never had it before? It will regress performance a bit for sure, but I believe it can also introduce functional breaks or memory leaks. |
I'll continue to voice the contrarian view I've held for ages. :) IMO the dispose pattern ( "But what if a subclassed type wants to maintain an unmanaged handle?" is sometimes brought up as a counterargument. I'm not too worried about this for two reasons. First, I anticipate very few people ever subclassing an existing unsealed type and using raw handles instead of managed handles. Second, on the off-chance that somebody does this, they'll need to write a finalizer anyway, and they can take that opportunity to clean up whatever unmanaged resources they were holding on to. IMO there's no benefit to forcing types all the way up and down the hierarchy to worry about this unlikely scenario. |
@GrabYourPitchforks: What do you think about unregistering events? What is the suggested pattern here? If an object with registered event handlers gets disposed should the class itself unregister by setting the events to null, or should the one the registered the event also unregister it? |
You'll see some discrepancies on that across the Framework. UI stacks in particular often have their own mechanisms for registering and deregistering. As a rule of thumb, I'd recommend that the component responsible for registering the event also be responsible for unregistering the event. If I instantiate some object Foo, then I call But that's just a rule of thumb. If you're building a component for a particular UI framework, general recommendation is to follow whatever convention that UI framework prescribes. That'll make it easier for your component to interoperate with its sibling components and for consumers to reason about your component's behavior. |
Did we get into the consensus here? Regardless of the output it doesn't sound like this will make the 6.0. Please move back if you think it will. |
Continuing the discussion from here: #41918 (comment)
@eerhardt pointed out that our IDisposable docs indicate that we should call GC.SuppressFinalize even if we don't have a finalizer, so that derived classes don't need to call it.
For this to work, we have to always call this, otherwise folks adding the finalizer need to check if the base type is already calling it (by looking at source or disassembling) and then only call it if the base type doesn't call it. I don't think we should make folks look at implementations (nor can they in all cases). So the alternate safe advice is to always call GC.SF in Dispose(bool) the type that adds the finalizer.
I think relying on the base-type that implements IDisposable to call it is the best recommendation since it results in the fewest possible calls to GC.SF. However for this to work, we need to do it everywhere.
Currently we don't. I spot checked a few places:
runtime/src/libraries/System.Private.CoreLib/src/System/IO/BinaryWriter.cs
Lines 86 to 89 in b910310
runtime/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs
Lines 67 to 74 in b910310
runtime/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs
Lines 1249 to 1252 in b910310
runtime/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs
Lines 140 to 148 in b910310
runtime/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceSet.cs
Lines 82 to 105 in b910310
The text was updated successfully, but these errors were encountered: