Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way of implementing Finalize and Dispose(When parent class implements IDisposable)

I was implementing Finalize and Dispose in my classes, I implemented IDisposable on my parent class and override the Dispose(bool) overload in my child classes. I was not sure

  1. whether to use a duplicate isDisposed variable(as its already there in base class) or not?
  2. Whether to implement a finalizer in child class too or not?

Both these things are done in example given here -

http://guides.brucejmack.biz/CodeRules/FxCop/Docs/Rules/Usage/DisposeMethodsShouldCallBaseClassDispose.html

Whereas example in this MSDN article doesn't have any of these two - http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx

whereas this example in MSDN is not complete - http://msdn.microsoft.com/en-us/library/ms182330.aspx

like image 375
akjoshi Avatar asked Jan 20 '23 22:01

akjoshi


1 Answers

It's very rare for a finalizer to be useful. The documentation you link to isn't totally helpful - it offers the following rather circular advice:

Implement Finalize only on objects that require finalization

That's an excellent example of begging the question, but it's not very helpful.

In practice, the vast majority of the time you don't want a finalizer. (One of the learning curves .NET developers have to go through is discovering that in most of the places they think they need a finalizer, they don't.) You've tagged this as (amongst other things) a WPF question, and I'd say it'd almost always be a mistake to put a finalizer on a UI object. (So even if you are in one of the unusual situations that turns out to require a finalizer, that work doesn't belong anywhere near code that concerns itself with WPF.)

For most of the scenarios in which finalizers seem like they might be useful, they turn out not to be, because by the time your finalizer runs, it's already too late for it to do anything useful.

For example it's usually a bad idea to try to do anything with any of the objects your object has a reference to, because by the time your finalizer runs, those objects may already have been finalized. (.NET makes no guarantees about the order in which finalizers run, so you simply have no way of knowing whether the objects you've got references to have been finalized.) It's bad idea to invoke a method on an object whose finalizer has already been run.

If you have some way of knowing that some object definitely hasn't been finalized, then it is safe to use it, but that's a pretty unusual situation to be in. (...unless the object in question has no finalizer, and makes use of no finalizable resources itself. But in that case, it's probably not an object you'd actually need to do anything to when your own object is going away.)

The main situation in which finalizers seem useful is interop: e.g., suppose you're using P/Invoke to call some unmanaged API, and that API returns you a handle. Perhaps there's some other API you need to call to close that handle. Since that's all unmanaged stuff, the .NET GC doesn't know what those handles are, and it's your job to make sure that they get cleaned up, at which point a finalizer is reasonable...except in practice, it's almost always best to use a SafeHandle for that scenario.

In practice, the only places I've found myself using finalizers have been a) experiments designed to investigate what the GC does, and b) diagnostic code designed to discover something about how particular objects are being used in a system. Neither kind of code should end up going into production.

So the answer to whether you need "to implement a finalizer in child class too or not" is: if you need to ask, then the answer is no.

As for whether to duplicate the flag...other answers are providing contradictory advice here. The main points are 1) you do need to call the base Dispose and 2) your Dispose needs to be idempotent. (I.e., it doesn't matter if it's called once, twice, 5 times, 100 times - it shouldn't complain if it's called more than once.) You're at liberty to implement that however you like - a boolean flag is one way, but I've often found that it's enough to set certain fields to null in my Dispose method, at which point that removes any need for a separate boolean flag - you can tell that Dispose was already called because you already set those fields to null.

A lot of the guidance out there on IDisposable is extremely unhelpful, because it addresses the situation where you need a finalizer, but that's actually a very unusual case. It means that lots of people write a IDisposable implementations that are far more complex than necessary. In practice, most classes call into the category Stephen Cleary calls "level 1" in the article that jpierson linked to. And for these, you don't need all the GC.KeepAlive, GC.SuppressFinalize, and Dispose(bool) stuff that clutters up most of the examples. Life's actually much simpler most of the time, as Cleary's advice for these "level 1" types shows.

like image 152
Ian Griffiths Avatar answered Jan 23 '23 10:01

Ian Griffiths