Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding disposing system-defined Pen and Brush instances

I understand it is best practise to call Dispose() on instances of Pen and Brush, except if they've been set to the system-predefined values (eg. System.Drawing.Brushes, System.Drawing.Pens or System.Drawing.SystemBrushes)

Trying to dispose a system-defined resource results in an exception being thrown.

It doesn't appear to be obvious how you can detect (apart from wrapping the Dispose() call in a try/catch) whether one of these resources is referencing a system-defined or user-defined value.

like image 954
David Gardiner Avatar asked Jul 08 '10 04:07

David Gardiner


People also ask

What happens if you dont Dispose IDisposable?

IDisposable is usually used when a class has some expensive or unmanaged resources allocated which need to be released after their usage. Not disposing an object can lead to memory leaks.

When Dispose method is called?

Dispose method rules are as follows: Used for unmanaged resources requiring immediate release after use. If Dispose is not called, the Finalize method should be implemented. After calling the Dispose method, the GC. SuppressFinalize method must be called to avert the Finalize method and avoid unnecessary GC.

Is Dispose method called automatically?

Dispose() will not be called automatically. If there is a finalizer it will be called automatically. Implementing IDisposable provides a way for users of your class to release resources early, instead of waiting for the garbage collector.

Why do we need to implement a disposed method with an implement destructor in C#?

Because you can't control when (or even if) the GC calls Finalize, you should treat destructors only as a fallback mechanism for releasing unmanaged resources. Instead, the approved way to release unmanaged resources is to make your class inherit from the IDisposable interface and implement the Dispose() method.


2 Answers

This is an old question I know, but I've been doing some research into resource leaks involving GDI objects, and almost every statement in the accepted answer is false. In the interests of accuracy for later readers who find this question via a search, as I did:

There is no requirement to call Dispose.

This statement is misleading. Though technically you can get away with not calling Dispose, it is an extremely poor practice to not dispose of a brush or pen that you own once you're done with it.

The reason is: there is a hard limit (set to ten thousand by default, though that can be increased via registry hacks) of the number of GDI objects that can be outstanding in a process -- not application domain, note -- and the garbage collector might not get around to finalizing resources faster than they are being leaked. Managed memory allocations produce collection pressure but there is no corresponding finalization pressure.

The purpose of garbage collection is to eliminate these kinds of requirements.

It is not. The purpose of garbage collection is to emulate an environment with arbitrarily much memory.

One of the main purposes of IDisposable is to allow a class to clean up unmanaged resources in resource-limited environments.

This is correct.

If you do not call the Dispose() method, the unmanaged resources of the class will be cleaned up once the object is finialized and disposed during garbage collection.

This is sometimes correct; it is correct for GDI objects. It is a good practice for classes which hold on to unmanaged resources to implement finalization semantics as a backstop; an extra layer of protection for people who follow the bad advice given in the accepted answer. You should not rely on finalizers to save you from your mistakes; you should dispose of your resources.

You should only rely on a finalizer to deal with crazy exceptional situations. Consider for example:

Brush myBrush = MakeMeABrush();
DoSomethingWithBrush(myBrush);
myBrush.Dispose();

Suppose a thread abort exception is thrown after the allocation of the brush but before the assignment to myBrush. No combination of try-catch-finally will enable you to clean up that brush via Dispose; you'll have to rely upon the finalizer. That's what the finalizer is for: the completely crazy situations where you cannot dispose yourself. It is not an excuse to be sloppy.

If you "must" call dispose and you do not know if the brush instance is a "system-" or a "normal-" brush then you will have to use a try...catch block.

Though this is again, technically correct, it misses the point completely. If you are in a situation where you do not know whether or not you own the brush then you have a bug in your program design. Do not paper over your bugs with a try-catch block! Fix the bug!

This situation is common to all explicitly-managed resources: the entity which provides the resource and the entity which consumes the resource are responsible for clearly documenting which of the two owns cleaning up the resource. If you don't know whether you own the brush that you've been given or not then someone isn't doing a task they were responsible to do, namely, preventing that situation from ever arising.

If the contract you decide upon is that the entity which provides the resource is responsible for cleaning it up later then your consumer shouldn't be disposing of the brush at all, because that is breaking the contract; the producer will clean that up if it needs to.

If the contract you decide upon is that both the producer and consumer are going to free the resource, then the consumer must call Clone on every brush passed in to ensure that they have a safely disposable resource, and that the producer continues to own a valid resource as well.

If, most likely, the contract you decide upon is that the entity which is consuming the resource is responsible for cleaning it up later then the provider is required to always hand you a brush that you can safely dispose, and is required to not dispose the resource itself. Since the provider knows whether they made the brush themselves or got it from the system, the provider must call Clone() on system brushes to obtain a brush that can be safely disposed, and then pass that to the consumer.

But the contract "no one cleans it up and we hope for the best" is a pretty poor contract.

It is not needed to call Dispose() on SystemBrushes and SystemPens because the GDI+ Library will take care of these resources.

This explanation is an explanation that doesn't actually explain anything. The reason why it is illegal to dispose of one of these brushes is because the lifetime of the brush is equal to the lifetime of the appdomain.

The Remarks section of the class will make note of if there is a requirement to call the Dispose method.

This statement is false. You should make the assumption that it is always necessary to Dispose an IDisposable resource unless you have good reason to believe otherwise. The absence of a line in the documentation is not evidence that disposing is unnecessary.

To end on a positive note:

If I have a graphic-intensive application or class then I will cache instances of the pens and brushes I need. I use them throughout the life of the application or class.

This is a good practice. If the number of brushes and pens created is relatively small and the same ones are being used over and over again, then it makes good sense to cache them permanently. Since their lifetimes are to the end of the program there is no need to dispose them. In this case we are not disposing the garbage because it's not garbage; it's useful. GDI objects that are not cached should of course still be disposed. Again, pursuing a caching strategy is not an excuse to engage in poor practices.

like image 99
Eric Lippert Avatar answered Jan 02 '23 19:01

Eric Lippert


When you are finished with a Graphics object that you create, you must dispose of it by calling its Dispose method. (This rule is true for many different GDI+ objects.) Don't keep it around for a rainy day because it won't be valid later. You must, must, must dispose of it when you are finished with it. If you don't, it could result in image corruption, memory usage issues, or worse yet, international armed conflict. So, please dispose of all Graphics objects properly.

If you create a Graphics object within an event, you really need to dispose of it before exiting that event handler. There is no guarantee that the Graphics object will still be valid in a later event. Besides, it's easy to re-create another Graphics object at any time.

Got from here http://msdn.microsoft.com/en-us/library/orm-9780596518431-01-18.aspx

like image 43
Bug Avatar answered Jan 02 '23 21:01

Bug