Skip to content
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

Close() should NEVER call Dispose() #111258

Open
autoany opened this issue Jan 10, 2025 · 3 comments
Open

Close() should NEVER call Dispose() #111258

autoany opened this issue Jan 10, 2025 · 3 comments
Labels
area-System.ComponentModel untriaged New issue has not been triaged by the area owner

Comments

@autoany
Copy link

autoany commented Jan 10, 2025

I apologyze for not knowing how to properly adress this thing, please forward where appropriate.

Close() should NEVER call Dispose()
Dispose() or Dispose(bool) CAN call Close() but NEVER the other way around!

These (C/D) are two entirely different things, be it file or COM port or anything, especially when it is derived from Component.

There is bigger story about this, but the main logic is:
Close should not Dispose, because Dispose can (and often does) more things like GC.SuppressFinalize

How are you supposed to re-Open it? That should work, right?


....and ~Finalize() should never throw an exception, right?

Reality about SerialPort at least until .NET 4.5 when you unplug the USB - the whole App crashes and there was no defence.
At lest that got solved I hope:

But wait, this gets called from Close as well, right?


((EventHandler?)_events[s_eventDisposed])?.Invoke(this, EventArgs.Empty);

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jan 10, 2025
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-componentmodel
See info in area-owners.md if you want to be subscribed.

@huoyaoyuan
Copy link
Member

Close() should NEVER call Dispose() Dispose() or Dispose(bool) CAN call Close() but NEVER the other way around!

These (C/D) are two entirely different things

They are usually alias, for example Stream.Dispose simply calls Close. Calling which from the other is just implementation detail.

Close should not Dispose, because Dispose can (and often does) more things like GC.SuppressFinalize

This is also implementation detail of the disposable pattern. Close, Dispose and finalizer are all doing the same thing. GC.SuppressFinalize is just an optimization to avoid duplicated check. It's also a standard part of the dispose pattern. You can learn more at https://learn.microsoft.com/dotnet/standard/design-guidelines/dispose-pattern and https://learn.microsoft.com/dotnet/standard/garbage-collection/implementing-dispose .

How are you supposed to re-Open it? That should work, right?

Re-opening is usually not a thing. Reusable objects must be specially designed other than non-reusable ones. For really-reusable objects like DbContext, Dispose will also reset its state for reuse.
Specially for GC.SuppressFinalize, there is GC.ReregisterForFinalize.

....and ~Finalize() should never throw an exception, right?

Ideally it should, but it's impractical to really get exception-free when operating with external resource. The common practice is to not let it run, by calling Dispose properly.

Reality about SerialPort at least until .NET 4.5 when you unplug the USB - the whole App crashes and there was no defence.

Any IO operation like network can get exception from physical connection lost, and not properly handling the exception will crash application. How is it related to Close or Dispose?

@autoany
Copy link
Author

autoany commented Jan 10, 2025

Re-opening is usually not a thing.

I have to disagree. SerialPort is Component you can put onto a Form, then assign a port, Open, Close, assign different port, Open, Close, ...reuse. Why not?

....and ~Finalize() should never throw an exception, right?

Ideally it should, but it's impractical to really get exception-free when operating with external resource. The common practice is to not let it run, by calling Dispose properly.

Reality about SerialPort at least until .NET 4.5 when you unplug the USB - the whole App crashes and there was no defence.

Any IO operation like network can get exception from physical connection lost, and not properly handling the exception will crash application. How is it related to Close or Dispose?

No Close or Dispose ever helped with SerialPort in .NET 2.0 upto .NET 4.0 (at least 3.5) when you unplugged the USB (with VCP), because it threw exception from finalizer in GC-thread, which crashed the app, no matter what you did.

EDIT:

there is GC.ReregisterForFinalize

See no such thing in Open

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.ComponentModel untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

2 participants