Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
166 views
in Technique[技术] by (71.8m points)

c# - Dispose, when is it called?

Consider the following code:

namespace DisposeTest
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Calling Test");

            Test();

            Console.WriteLine("Call to Test done");
        }

        static void Test()
        {
            DisposeImplementation di = new DisposeImplementation();
        }
    }

    internal class DisposeImplementation : IDisposable
    {
        ~DisposeImplementation()
        {
            Console.WriteLine("~ in DisposeImplementation instance called");
        }
        public void Dispose()
        {
            Console.WriteLine("Dispose in DisposeImplementation instance called");
        }
    }
}

The Dispose just never get's called, even if I put a wait loop after the Test(); invocation. So that quite sucks. I want to write a class that is straightforward and very easy to use, to make sure that every possible resource is cleaned up. I don't want to put that responsibilty to the user of my class.

Possible solution: use using, or call Dispose myself(basicly the same). Can I force the user to use a using? Or can I force the dispose to be called?

Calling GC.Collect(); after Test(); doesn't work either.

Putting di to null doesn't invoke Dispose either. The Deconstructor DOES work, so the object get's deconstructed when it exits Test()

Ok guys, it's clear now!

Thank you all for your answers! I will add a warning in the comment!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

A couple of important points should be made to address the OP's question:

  1. .NET GC is non-deterministic (i.e. you never know nor should you depend on when it happens)
  2. Dispose is never called by the .NET Framework; you must call it manually - preferably by wrapping its creation in a using() block.
  3. Explicitly setting a disposable object to null without calling Dispose() on it is a bad thing to do. What happens is that you explicitly set the objects "root reference" to null. This effectively means that you cannot call Dispose later AND more importantly, it sends the object to the GC Finalization Queue for Finalization. Causing Finalization by bad programming practice should be avoided at all costs.

Finalizer: Some developers refer to it as a destructor. And in fact it is even called a Destructor in the C# 4.0 Language Spec (section 1.6.7.6) and in previous versions of the current ECMA-334 spec. Fortunately, the 4th Edition (June 2006) correctly defines Finalizers in Section 8.7.9 and attempts to clear up the confusion between the two in Section 17.12. It should be noted that there are important internal differences (no need to go into those gory details here) between what is traditionally known as a destructor and a Destructor/Finalizer in the .NET Framework.

  1. If a Finalizer is present, then it will be called by the .NET Framework if and only if GC.SuppressFinalize() is not called.
  2. You should NEVER explicitly call a finalizer. Fortunately, C# will not explicitly allow this (I don't know about other languages); though it can be forced by calling GC.Collect(2) for the 2nd generation of the GC.

Finalization: Finalization is the .NET Framework's way to deal with the 'graceful' cleanup and releasing of resources.

  1. It only occurs when there are objects in the Finalization Queue.
  2. It only occurs when a garbage collection occurs for Gen2 (which is approx 1 in every 100 collections for a well-written .NET app).
  3. Up to and including .NET 4, there is a single Finalization thread. If this thread becomes blocked for any reason, your app is screwed.
  4. Writing proper and safe finalization code is non-trivial and mistakes can be made quite easily (i.e. accidently allowing exceptions to be thrown from the Finalizer, allowing dependencies on other objects that could already be finalized, etc.)

While this is certainly more info that you asked for, it provides background on how things work and why they work the way they do. Some people will argue that they shouldn't have to worry about managing memory and resources in .NET, but that doesn't change the fact that it needs to be done - and I don't see that going away in the near future.

Unfortunately, the examples above (mistakenly) imply that you need to implement a Finalizer as part of the standard Dispose pattern. However, you should not implement a Finalizer unless you are using UNmanaged code. Otherwise, there are negative performance implications.

I have posted a template for implementing the Dispose pattern here: How do you properly implement the IDisposable pattern?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...