文章目录
  1. 1. Implementing a Dispose Method
  2. 2. Overriding the Finalize Method
  3. 3. Using the using statement

According the last blog(Here), destructor cannot be called by programmers , it is invoked automatically by GC in a uncertain time.So whether it means programmers need not care about releasing resource(or GC) in C#.

In general, C# do not require as much memory management as is need.This is because the .NET Framework Garbage Collector implicitly manages the allocation and release of memory for your application.However most time, a explicit and accuracy way that programmers can release memory by themselves is necessary.For example, when your application encapsulate unmanaged resources such as a window, file, font, database connection, network connection, and so on, you will find it very useful and helpful.So how many different and explicit ways can clean up the rare and expensive external resources that using by your application?

Three ways provided on MSDN,for more details about them, see the following parts.

Implementing a Dispose Method

The IDispose Interface provide Dispose Method,it is used only for unmanaged resource. Do not implement and call it, unless application has encapsulate some unmanaged resources.For all managed resources, the Garbage Collector has a very efficient and implicit way to manage them,please never do the idle work.

A class’s Dispose method should release all resources that it owns and it should also clean up all resources owned by its parent class by calling the parent’s Dispose method,and then, the parent will continue to invoke its base’s Dispose method.To help ensure all resources are always released completely, a Dispose method should be called several times without throwing any exceptions.Do not forget to call SuppressFinalize function in the Dispose,it will prevents the Finalize method from being invoked if current object is on the finalization queue.For more details about Finalize method will be described in the next title, and please remember that Finalize method called by GC automatically and executing a Finalize method is costly to performance.If a Dispose method was called for release resources,it is not necessary for the Garbage Collector to called the disposed object’s Finalize again.

The following example shows how to implement a Dispose method is a good pattern.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
namespace CSnippets.GC
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;

    public class DisposeExample
    {
        public static void TestDispose()
        {
            try
            {
                string path = Environment.CurrentDirectory + "\\App.config";

                FileStream fs = File.OpenRead(path);

                DisposableResource dr = new DisposableResource(fs);
                
                //
                dr.DoSomethingWithResource();

                //
                dr.Dispose();
            }
            catch (FileNotFoundException expt)
            {
                Console.WriteLine(expt.Message);
            }
        }
    }

    public class DisposableResource : IDisposable
    {
        private Stream resource = null;
        private bool disposed = false;

        public DisposableResource(Stream stream)
        {
            if (null == stream)
            {
                throw new ArgumentNullException("stream is null ...");
            }

            if (!stream.CanRead)
            {
                throw new ArgumentException("stream must be readable ...");
            }

            resource = stream;
            disposed = false;
        }

        public void DoSomethingWithResource()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("resource was disposed ...");
            }

            int num = (int)resource.Length;
            Console.WriteLine("Number of bytes: {0}", num.ToString());
        }

        public void Dispose()
        {
            Dispose(true);

            //  Using SuppressFinalize in case a subclass
            //  of this type implements a finalizer.
            System.GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            // If you need thread safety, use a lock around these 
            // operations, as well as in your methods that use the resource.
            if (!disposed)
            {
                if (disposing)
               {
                    // Release unmanaged resource

                    if (null != resource)
                    {
                        resource.Dispose();
                        Console.WriteLine("Object disposed ...");
                    }
                }

                // Release managed resource

               // Indicate that the instance has been disposed.
                resource = null;
                disposed = true;
            }
        }
    }
}

Actually,sometimes Close method is also used to release resources such as FileStream.Close.

Overriding the Finalize Method

Finalize is a protected method provided by the base class Object, so implement the Finalize method in C#, you must use destructor syntax.It is the same as Dispose method, a Finalize method should release all resources its owned and its parent owned by calling its base’s Finalize method.

By default, the Object.Finalize method does nothing,unless override it by yourself.For example, if you want to clean up all resources before system executes the GC, you must override it in your class.and more important thing, you must ensure that a Finalize method should not throw any exceptions, because the application can not handle it and it can cause the application to terminate.

Example is the best documents for programmer, is it right?Please see the below code snippets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// Design pattern for a base class.
public class Base: IDisposable
{
    private bool disposed = false;
 
    //Implement IDisposable.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
	          {
            if (disposing)
            {
                // Free other state (managed objects).
            }
            // Free your own state (unmanaged objects).
            // Set large fields to null.
            disposed = true;
        }
    }
 
    // Use C# destructor syntax for finalization code.
    ~Base()
    {
        // Simply call Dispose(false).
        Dispose (false);
    }
}
 
// Design pattern for a derived class.
public class Derived: Base
{
    private bool disposed = false;
 
    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Release managed resources.
            }
            // Release unmanaged resources.
            // Set large fields to null.
           // Call Dispose on your base class.
            disposed = true;
        }
        base.Dispose(disposing);
    }
    // The derived class does not have a Finalize method
    // or a Dispose method without parameters because it inherits
    // them from the base class.
}

Aha, it looks same as the topic how to implement a Dispose method except the yellow part.Why does it need to do like that?

Actually, Even when you provide explicit calling Dispose method, you should invoke the Finalize method implicitly.The Finalize provide a backup to prevent resources from permanently leaking if the programmer fails to call Dispose method, even executing a Finalize method is an expensive operation.

So what is your opinion?Do you think it is one way and one completely way that do those two ways together to release resources?

Using the using statement

Using statement provide a simple and convenient syntax to ensure that a correct way is be used for a IDispose object.It can make sure that Dispose method is called even if an exception occurs.For example, for StreamWriter, the using statement automatically closes the stream and invokes Dispose method on the StreamWriter object at the end of using statement.

Below is a good practice that shows how to use using statement to clean up resources with Font(System.Drawing.Font) object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using (Font ft = new Font("Arial", 15.0f))
{
    byte charset = ft.GdiCharSet;

    Console.WriteLine("Charset of Arial is {0}", charset);
}

Font ft2 = new Font("Arial", 10.0f);
using (ft2) // not recommended
{
    // use font2
    byte charset2 = ft2.GdiCharSet;

    Console.WriteLine("Charset 2 of Arial is {0}", charset2);
}

// font2 is still in scope
// but the method call throws an exception
float f = ft2.GetHeight();

Of course, it is a much more complex topic of Garbage Collection in C# and there is several times contents about GC on MSDN than here.More details and comprehensive contents about Garbage Collection,see this webpage.have fun!

文章目录
  1. 1. Implementing a Dispose Method
  2. 2. Overriding the Finalize Method
  3. 3. Using the using statement