Correct implementation of IDisposable
The interface used to release object resources in .NET is IDisposable, but the implementation of this interface is quite particular. In addition, there are two functions: Finalize and Close.
MSDN recommends implementing the IDisposable interface according to the following pattern:
1 public class Foo: IDisposable
2 {
3 public void Dispose()
4 {
5 Dispose(true);
6 GC.SuppressFinalize(this);
7}
8
9 protected virtual void Dispose(bool disposing)
10 {
11 if (!m_disposed)
12 {
13 if (disposing)
14 {
15 // Release managed resources
16}
17
18 // Release unmanaged resources
19
20 m_disposed = true;
twenty one }
twenty two }
twenty three
24 ~Foo()
25 {
26 Dispose(false);
27}
28
29 private bool m_disposed;
30}
31
32
There are actually two functions for releasing resources in .NET objects: Dispose and Finalize. The purpose of Finalize is to release unmanaged resources, while Dispose is used to release all resources, including managed and unmanaged.
In this mode, the void Dispose(bool disposing) function uses a disposing parameter to distinguish whether it is currently being called by Dispose(). If it is called by Dispose(), both managed and unmanaged resources need to be released at the same time. If it is called by ~Foo() (that is, C#'s Finalize()), then you only need to release the unmanaged resources.
This is because the Dispose() function is explicitly called by other code and requires the release of resources, while Finalize is called by the GC. Other managed objects referenced by Foo may not need to be destroyed when the GC is called, and even if they are destroyed, they will be called by the GC. Therefore, only unmanaged resources need to be released in Finalize. On the other hand, since managed and unmanaged resources have been released in Dispose(), it is not necessary to call Finalize again when the object is recycled by GC, so call GC.SuppressFinalize(this) in Dispose() to avoid duplication Call Finalize.
However, there is no problem even if Finalize and Dispose are called repeatedly, because of the existence of the variable m_disposed, the resource will only be released once, and redundant calls will be ignored.
Therefore, the above pattern guarantees:
1. Finalize only releases unmanaged resources;
2. Dispose releases managed and unmanaged resources;
3. There is no problem in calling Finalize and Dispose repeatedly;
4. Finalize and Dispose share the same resource release strategy, so there is no conflict between them of.
In C#, this pattern needs to be implemented explicitly, where C#'s ~Foo() function represents Finalize(). In C++/CLI, this mode is automatically implemented, but the C++ class destructor is different.
According to C++ semantics, the destructor is called when it goes out of scope or deletes. In Managed C++ (that is, managed C++ in .NET 1.1), the destructor is equivalent to the Finalize() method in the CLR, which is called by the GC during garbage collection. Therefore, the timing of the call is unclear. In C++/CLI in .NET 2.0, the semantics of destructors were modified to be equivalent to the Dispose() method, which implies two things:
1. All CLR classes in C++/CLI implement the interface IDisposable, so you can use the using keyword to access instances of this class in C#.
2. The destructor is no longer equivalent to Finalize().
For the first point, this is a good thing, I think semantically Dispose() is closer to the C++ destructor. Regarding the second point, Microsoft has made an extension by introducing the "!" function, as shown below:
1 public ref class Foo
2 {
3 public:
4Foo();
5 ~Foo(); // destructor
6 !Foo(); // finalizer
7};
8
The "!" function (I really don't know what to call it) replaces the original Finalize() in Managed C++ and is called by the GC. MSDN recommends that in order to reduce code duplication, you can write code like this:
1 ~Foo()
2 {
3 //Release managed resources
4 this->!Foo();
5}
6
7 !Foo()
8 {
9 //Release unmanaged resources
10}
11
For the above class, the corresponding C# code generated by C++/CLI is actually as follows:
1 public class Foo
2 {
3 private void !Foo()
4 {
5 // Release unmanaged resources
6}
7
8 private void ~Foo()
9 {
10 // Release managed resources
11 !Foo();
12}
13
14 publicFoo()
15 {
16}
17
18 public void Dispose()
19 {
20 Dispose(true);
21 GC.SuppressFinalize(this);
twenty two }
twenty three
24 protected virtual void Dispose(bool disposing)
25 {
26 if (disposing)
27 {
28 ~Foo();
29 }
30 else
31 {
32 try
33 {
34 !Foo();
35}
36 finally
37 {
38 base.Finalize();
39 }
40}
41 }
42
43 protected void Finalize()
44 {
45 Dispose(false);
46 }
47 }
48
Since ~Foo() and !Foo() will not be called repeatedly (at least MS thinks so), there are no variables in this code that are the same as the previous m_disposed, but the basic structure is the same.
Moreover, you can see that it is not actually ~Foo() and !Foo() that are Dispose and Finalize, but the C++/CLI compiler generates two Dispose and Finalize functions and calls them at the appropriate time. C++/CLI has actually done a lot of work, but the only problem is that it relies on the user calling !Foo() in ~Foo().
Regarding resource release, the last thing to mention is the Close function. It is semantically very similar to Dispose. According to MSDN, this function is provided to make users feel more comfortable, because users are more accustomed to calling Close() for certain objects, such as files.
However, after all, these two functions do the same thing, so the code recommended by MSDN is:
1 public void Close()
2 {
3 Dispose(();
4}
5
6
Here, the Dispose function without parameters is called directly to obtain the same semantics as Dispose. This seems to be perfect, but on the other hand, if Dispose and Close are provided at the same time, it will bring some confusion to users. Without seeing the code details, it is difficult to know the difference between these two functions. Therefore, the .NET code design specifications say that users can actually only use one of these two functions. So the suggested pattern is:
1 public class Foo: IDisposable
2 {
3 public void Close()
4 {
5 Dispose();
6}
7
8 void IDisposable.Dispose()
9 {
10 Dispose(true);
11 GC.SuppressFinalize(this);
12}
13
14 protected virtual void Dispose(bool disposing)
15 {
16 // Same as before
17}
18}
19
A so-called explicit implementation of the interface is used here: void IDisposable.Dispose(). This explicit implementation is only accessible through the interface, but not through the implementing class. therefore:
1 Foo foo = new Foo();
2
3 foo.Dispose(); // Error
4 (foo as IDisposable).Dispose(); // Correct
5
This takes care of both. For those who like to use Close, they can use foo.Close() directly, and they will not see Dispose(). For those who like Dispose, he can convert the type to IDisposable to call, or use the using statement. Great joy to both!
http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html