views:

281

answers:

4

I know that in C#, if you write ~MyClass(), this basically translates to override System.Object.Finalize(). So, whether you write the destructor or not, every type in CLR will have a Finalize() method in it (of System.Object at least).

1] So, does it mean that, every object, by default, has a finalizer ?

2] What is the basis for the CLR to decide that an object should be put through finalization queue ?

I'm asking this, because, I had a class, say ManagedResourceHolder that implemented IDisposable, but did not call GC.SuppressFinalize(this) in its IDisposable.Dispose() method. The class did not hold any unmanaged resources, and there was no need for the ~ManagedResourceHolder() method, which in turn meant no need for the GC.SuppressFinalize(this) call as there was no finalizer.

3] In context of the above scenario, is it always necessary to provide a finalizer when you implement IDisposable ? (even on a class that holds no unmanaged resources)

The FxCop rule CA1816 was giving me a violation on this and the response I got here when I asked in the CA forum on MSDN confused me.

Thanks.

A: 
  1. Nope, it doesn't mean so. Only overridden Finalize() will count by the CLR.
  2. By having a finalizer, as defined above.
  3. Nope, it's not always necessary. It's just a nice pattern. I mean, nobody forces you to do so. But it's a good thing to do if you have unmanaged resources, since if somebody forgets to dispose it, the unmanaged resource will get released sometime. FxCop does not enforce strict rules. It enforces good patterns that may lead to failure in the future if you don't take care of.

UPDATE: Every class is responsible for managing its own resources. Due to abstraction and encapsulation features of object-oriented paradigms, the consumer of a class should not care about what resources it has, indirectly. Therefore, you should either manually release the resources you own (what you own is what you directly own as you look at other things as a black box) or leave it to the GC to release them. For unmanaged resources, you don't have the option to leave it to the GC, so you have to release it manually. In this sense, a SafeHandle that Jon mentioned is a managed abstraction of an unmanaged resource, therefore it should be treated as a valuable managed resource (which is a black box that manages the finalization of that unmanaged resource itself).

Mehrdad Afshari
It's not a nice pattern to add a finalizer BECAUSE you're IDisposable. It's an expensive mistake. There are lots of reasons to be IDisposable which don't indicate the use of a finalizer.
Will Dean
I think I mentioned "if you have unmanaged resources" in the answer.
Mehrdad Afshari
Yes, but you need to make it clear that it's only if you *directly* have an unmanaged resource. For instance, if you've just got a FileStream, let that finalize itself - you don't need to. You should very rarely, if ever, have your own direct handles these days. Use SafeHandle.
Jon Skeet
I don't think a managed class is an unmanaged resource, is it? I count FileStream as a managed resource and I don't really care what it holds. But when I create something in the unmanaged world myself, it is definitely an unmanaged resource.
Mehrdad Afshari
@Mehrdad: Then I think you should clarify your answer. It's a matter of directly holding an unmanaged resource vs indirectly holding it. If I have a FileStream reference, I indirectly have an unmanaged resource - which is why I should call Dispose on it.
Jon Skeet
Jon, surely, your opinion is too valuable to ignore! I will, immediately!
Mehrdad Afshari
@Mehrdad: Never do things just because of who's suggesting it, unless they're your boss or your significant other :) That's not to say my suggestion doesn't have merit - only that it should be judged on that merit rather than my rep :)
Jon Skeet
Of course, I didn't. If I did, I wouldn't argue with you. I respect you because you know what you are talking about, and yes, surely, great people make mistakes too. We're all here to learn!
Mehrdad Afshari
+9  A: 

Questions 1 and 2: The CLR basically checks whether or not the finalizer is overridden. If it's not, it treats it as not having a finalizer.

The benefit of having a finalizer in System.Object is that compilers know they can always put a call to base.Finalize() in. This avoids versioning issues. Consider a world without System.Object.Finalize():

  • System.Object (no Finalize)
  • Acme.BaseClass (no Finalize)
  • MyCompany.DerivedClass (Finalize)

Without a Finalize method in object, the finalizer in MyCompany.DerivedClass can't call anything. Which leads to a problem when version 2 of Acme.BaseClass comes out with a finalizer. Unless you recompile MyCompany.DerivedClass, an instance of DerivedClass will be finalized without calling BaseClass.Finalize, which is clearly a Bad Thing.

Now consider the same situation with System.Object.Finalize - the compiler inserts a call to base.Finalize automatically in DerivedClass.Finalize, which in version 1 just calls the no-op implementation in System.Object. When version 2 of Acme.BaseClass comes out, the call to base.Finalize will (without recompilation of DerivedClass) call BaseClass.Finalize.

Question 3: No, you don't need to have a finalizer just because you implement IDisposable. Finalizers should only be used for unmanaged resources which nothing else is going to clean up - i.e. ones you have a direct reference to. For instance, suppose you have a class which has a FileStream member variable. You want to implement IDisposable so you can close the stream as soon as possible, if the caller remembers - but if they don't remember to call Dispose(), the stream will become eligible for garbage collection at the same time as your object. Trust that FileStream has an appropriate finalizer (or a reference to something else with a finalizer etc) rather than trying to clean it up in your own finalizer.

As of .NET 2.0, with the SafeHandle class, it should be incredibly rare for you to need your own finalizer.

Jon Skeet
Am I correct in assuming that there is no need to call the GC.SuppressFinalize(this) in the IDisposable.Dispose() method in ManagedResourceHolder class (as it really has no finalizer to suppress) ?
mherle
Yup. No need to suppress what doesn't exist.
Jon Skeet
+3  A: 

1: It only really counts (in the useful sense) if it has been overridden

2: As defined by 1, and GC.SuppressFinalize has not been called (plus re-register etc)

3: certainly not; in fact, unless you are directly handling an unmanaged resource, you shouldn't have a finalizer. You shouldn't add a finalizer just because it is IDisposable - but things that have finalizers should also generally be IDisposable.

Marc Gravell
A: 

1) Yes (by virtue of inheritance)

2) Nothing is holding a reference to the instance of a class (that will make it eligible for finalization)

3) Yes (why should one implement IDisposable unless it is requiring the user to explicitly call dispose? Connection classes in .net use an umanaged resource under the hood & if you don't call dispose on it, it will hang on to it. Since the GC timing is unknown, the connection will remain open till that time)

This is my understanding.

I could be wrong. In which case, experts will correct things out for me.

shahkalpesh
You're wrong on the third point. If you *directly* hold unmanaged resources, you need a finalizer - but if you've just got a reference to something else and *that* has a finalizer, you don't gain anything from having one yourself. StreamWriter is a good example of this.
Jon Skeet
Jon, could you explain the above with an example? what does it mean by directly holding unmanaged resources (are you talking about connection class as an example)? Also, what does it mean by "if you've got a reference to something....".?
shahkalpesh
Jon, am I completely off-board in my understanding on all the points?
shahkalpesh
Have a look at the bottom of my answer. Basically if you've just got a reference to another object (e.g. a stream) you don't need a finalizer. If you've got an IntPtr which needs cleaning up, you should have a finalizer.
Jon Skeet
And actually, for 1 and 2 you're slightly off too. Everything has a Finalize method, but not everything *really* has a finalizer, as far as the CLR is concerned. Only objects which override Finalize are queued for finalization.
Jon Skeet