The correct way to implement Finalize and Dispose (When the parent class implements IDisposable)

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

  • Do I use the duplicate variable isDisposed (as it is already there in the base class) or not?
  • Do I need to implement a finalizer in a child class or not?

Both of these things are done in the example here -

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

While the example in this MSDN article does not have either of these two - http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx

while this example is not completed on MSDN - http://msdn.microsoft.com/en-us/library/ms182330.aspx

+4
source share
5 answers

It is very rare for a finalizer to be useful. The documentation you refer to is not entirely useful - it offers the following pretty cool tips:

Implementation Complete only objects that require finalization

This is a great example of begging, but it is not very helpful.

In practice, the vast majority of the time when you do not want a finalizer. (One of the learning curves that .NET developers should go through is to find that in most places they think they need a finalizer, they don’t.) You noted this as (among other things) a WPF question and I would say , which would almost always be a mistake to put the finalizer on the user interface object. (So, even if you are in one of the unusual situations, which, as it turned out, requires a finalizer, this work does not belong to anyone near the code that relates to WPF.)

For most scenarios in which finalizers look like they might be useful, they turn out to be different, because by the time your finalizer starts up, it's too late to do anything useful.

For example, it is usually a bad idea to try to do something with any of the objects referenced by your object, because by the time your finalizer starts, these objects can already be completed. (.NET makes no guarantees regarding the order in which finalizers are run, so you simply cannot find out if the objects you have links to have been finalized.) It's a good idea to call a method on an object whose finalizer has already been started.

If you have any way to find out that some object has definitely not been finalized, it is safe to use it, but this is a rather unusual situation. (... if the object does not have a finalizer, and does not use the finalized resources themselves. But in this case it is probably not the object that you really need to do when your own object leaves.)

The main situation in which finalizers seem useful is interop: for example, suppose you use P / Invoke to call some unmanaged API, and this API returns a handle to you. Perhaps there is another API that needs to be called in order to close this handle. Since all things are unmanageable, the .NET GC does not know what these descriptors are, and it is your job to make sure they are cleared, and at this point the finalizer is reasonable ... except in practice, it is almost always better to use SafeHandle for this scenario.

In practice, the only places I found using finalizers were: a) experiments designed to examine what the GC does, and b) diagnostic code designed to detect how specific objects are used in the system. None of the codes should go into production.

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

As for flag duplication ... the other answers here give conflicting advice. Highlights: 1) you need to call basic Dispose and 2) your Dispose should be idempotent. (That is, it doesn’t matter if he calls one, two, five, 100 times - he should not complain if he called more than once.) You can implement this as you wish - the Boolean flag in one case, but I often found that it is enough to set certain fields to null in my Dispose method, and at this point it eliminates the need for a separate Boolean flag - you can say that Dispose already called because you already set these fields to null .

Many of the recommendations on IDisposable extremely useless, as it concerns a situation where you need a finalizer, but this is really a very unusual case. This means that many people write IDisposable implementations that are much more complicated than necessary. In practice, most classes call the category "Stephen Cleary," called "level 1" in an article related to jpgerson . And for this, you do not need all the GC.KeepAlive , GC.SuppressFinalize and Dispose(bool) tags that clutter up most of the examples. Life is actually much simpler in most cases, as the Cleary tip for these level 1 types shows.

+6
source

Duplication required

If you don’t have a cleanup in the child class, just call base.Dispose() , and if there is a class level cleanup, do it after calling base.Dispose() . You need to separate the state of these two classes, so for each class there should be IsDisposed boolean. This way you can add a cleanup code when you need to.

When you define a class as IDisposable , you simply tell the GC that I am purging it, and you must SuppressFinilize in that class, so the GC will remove it from the queue. If you do not call GC.SupressFinalize(this) , nothing happens specifically for the IDisposable class. Therefore, if you implement it, as I mentioned, there is no need for a Finilizer , since you just told GC not to complete it.

+2
source

The correct way to implement IDisposable depends on whether you have unmanaged resources that belong to your class. The exact way to implement IDisposable is not all the developers, and some of them, such as Stephen Cleary , have strong opinions about the one-time paradigm in general.

see Finalize and Dispose implementation for cleaning unmanaged resources

The documentation for the IDisposable interface also explains this briefly and in this article points out some of the same things, but also on MSDN.

In the event that a duplicate logical field "isDisposed" is required in the base class. It seems like this is basically just a useful convention that you can use when the subclass itself can add additional unmanaged resources that need to be removed. Because Dispose is declared virtual, calling Dispose on an instance of a subclass always invokes a call to the method of the Dispose class, which in turn calls base.Dispose as the last step, allowing you to clear every level in the inheritance hierarchy. Therefore, I would probably summarize this, because if your subclass has additional unmanaged resources higher than what belongs to the database, then it would probably be best for you to have your own boolean isDisposed field to track it in a transactional mode inside it. Dispose method, but as Ian mentions in his answer, there are other ways to represent an already located state.

+1
source

1) No need to duplicate

2) The implementation of the finalizer will help destroy objects that are clearly not located. But not guaranteed . This is a good practice.

0
source

Use only the finalizer if the object contains information about materials that need to be cleaned, and this information is in some form different from references to objects to other objects that need to be cleaned (for example, a file descriptor stored as Int32). If a class implements a finalizer, it should not contain strong object references for any other objects that are not required for cleaning. If it will contain other links, the part responsible for cleaning should be divided into its object using the finalizer, and the main object should contain a link to this. The main object must not have a finalizer.

Derived classes should only have finalizers if the purpose of the base class was to support it. If the class’s goal is not centered around the finalizer, then there’s not much point in letting the derived class add it, since derived classes almost certainly should not (even if they need to add unmanaged resources, they should put the resources in their own class and just keep a reference to it )

0
source

Source: https://habr.com/ru/post/1332093/


All Articles