Docs says nothing unusual here:
Call Dispose when you are finished using the Font. The Dispose method leaves the Font in an unusable state. After calling Dispose, you must release all references to the Font so the garbage collector can reclaim the memory that the Font was occupying.
Designer assigning fonts this way: someControl.Font = new System.Drawing.Font(...); and never Dispose them.
In .net Font.Dispose(bool disposing) has an [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] attribute. But in core it's gone.
There are many questions about Font and Dispose on stackoverflow (with variants answers):
Should I dispose of the old font when changing the font of a control?
How and when is Font disposed for WinForms controls
Calling dispose() for Font in Windows form
Windows Form Fonts Questions Part 1
and more...
If nothing special here (docs are correct), why Designer never call Font.Dispose then? Otherwise, can we have official clarification (update docs)?
Ups, sorry about _api-suggestion_ label - my bad :( Pls remove it.
Fonts are dangerous to dispose because they can be shared, if you want desterminstic disposal you need a thourough concept of ownership to be sure its safe to dispose. The designer certainly doesn't have that. As far as I'm aware most of WinForms lets the GC clean up fonts, simply because its not safe to call Dispose when you don't know who else is still using it.
A better design would have been making a shallow clone of the managed font class when assigning it to a form/control, reference counting how many managed references you have to a native font. Then you could have eager disposal without running the risk of releasing the native font too early.
However thats too late to do, as it actually would decrease "memory performance" since you would have a lot more manged font allocations just in order to support deterministic cleanup. (They'd share the same native font but still cost managed allocations.) Personally I don't think its worth it, I never have seen a problem due to Fonts being cleaned up by GC too slow, I don't think its a resource bottleneck.
As it stands the GC is the only safe place to release fonts, manual disposal of Fonts is only safe if you understand the whole program structure and are sure its not shared anymore at point of disposal.
PS: Bitmaps and Icons have the same problem. For Bitmaps it may actually be worth to implement a reference counting scheme and eager disposal, because lazy disposal by GC can keep files locked. Though I guess not high priority since most people have learned to work around file locking by making in-memory copies of the bitmap if waiting for file unlocking by GC is an issue.
@weltkante Thank you. My thoughts on this lie roughly on the same plane.
So we definitely need some clarification on this in docs...
Thank you for the feedback. We don't technically own Font, it belong to .NET Runtime: https://github.com/dotnet/runtime/blob/master/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs
@JeremyKuhne do you know who would be the owner of System.Drawing.Common to pass the feedback to?
Tagging subscribers to this area: @safern, @tannergooding
Notify danmosemsft if you want to be subscribed.
@RussKie what is the action here? It seems like the question here is why the Designer never calls Dispose on usages of Font?
The docs clearly says:
Use Dispose, when you not need font any more.
But in WinForms, not a designer, no control (and it's right I think :) ) don't calls Dispose of a Font.
As @weltkante mentioned above for a good reason. And as you can see, many ppl got confused with this behaviour. I think, this questions (with different answers) are good summaries of this confusion.
My opinion: this is not isolated Font/WinForms question, this is about using Font in WinForms specific.
One course of action could be putting [EditorBrowsable(EditorBrowsableState.Never)] (and updating the docs, obviously) on font.Dispose(), if calling it could lead to these dangerous side-effects and the resources are released by the finalizer anyway.
what is the action here?
I'd like to see a (better) guidance on when/whether Font objects should be disposed.
Windows Forms constructs a few instances (and we're up for some guidance from our end, @merriemcgaw), but we also get a number of instances provided to us to .NET Runtime (e.g. via SystemFonts). Is it safe to call Dispose on those instances referenced from the Runtime?
I had a look at the Font docs but I came out none the wiser - the example raises the original question - why aren't fonts disposed of? We can say "because Label1 lives in the outer scope", but then why are we constructing Font instances every time event is raised?
I'm finding the Dispose docs somewhat vague too, e.g. what does this mean for a general developer?
_After calling Dispose, you must release all references to the Font so the garbage collector can reclaim the memory that the Font was occupying._
As to WinForms usage @weltkante called out:
As far as I'm aware most of WinForms lets the GC clean up fonts, simply because its not safe to call Dispose when you don't know who else is still using it.
Yes, we're stuck in WinForms as we don't have any idea when people aren't using them anymore as we expose them in public API.
There is confusion as Font doesn't dispose FontFamily which is IDisposable, but that hits the same case we generally have in WinForms (where System.Drawing originated). You can't dispose it as it hangs publicly off of Font.FontFamily.
The docs wording is a little confusing as @RussKie points out. I presume it was called out for the above reasons.
In general I would say that it is best practice to null out any fields for objects you have disposed. Font has a known, semi-documented need for it, but you should always do this as a matter of course to reduce rooting depth. That should hold true for Font- it should set the FontFamily field to null when it is disposed.
So those are the action items I see:
Font so it nulls out object fields on disposal (_fontFamily), which follows our own documented guidance, and perhaps makes explicitly calling it out moot