您的位置:首页 > 编程语言 > C#

Dispose and Finalize in C#

2011-12-14 11:05 260 查看

Introduction

This article provides an introduction to destructors in C# and reveals the non-deterministic destructor problem.

It also analyzes the
IDisposable
, garbage collection,
using
statement, etc.

Destructor

The destructor is a special purpose method in a class like a constructor. The constructor is called by the runtime after creating an instance. It is meant for initialization purposes. Like that, a destructor is called by the runtime before destroying an
instance. It is meant for finalization operations.

A destructor is declared like a constructor with a prefix tilde(~) in C#. A destructor do not have access modifiers like
public
,
private
, etc.

public class Shape

{

~Shape() // Destructor

{

}

}

In C#, the .NET runtime automatically destroys any class instances that are no longer in reference. It does thisduring the process of garbage collection.

Using of destructors will make the application slow in performance.

Finalizers

It is important to note that the destructors are also called Finalizers because the compiler converts a C# destructor into a method named
Finalize()
in the Intermediate Language (IL). The code in the destructor will be wrapped
in a
try..finally
block inside the
Finalize()
method.





Garbage Collection

This is the process of freeing up of unused memory of objects periodically. The .NET runtime does the process of garbage collection on the following conditions:

When there is shortage of memory
Developer invoked the
GC.Collect()
method

The garbage collection helps the developer to free up object instances.

But remember, garbage collection only destroys the managed resources, i.e. resources created inside the managed memory.

What is the Need of Destructors?

Since the runtime is managing the destruction of all objects, a question may arise: Why do we need destructors?

Answer: To destroy unmanaged resources.

The unmanaged resources are those created outside the managed environment. They may include file handles, network connections, etc. These resources created should be destroyed by the class itself, because the runtime is not able to destroy them.



Therefore, we use destructors to free up the unmanaged resources. This will ensure that all the unmanaged resources created by the class will be freed in the destructor.

E.g.: Let
Channel
be a class usingunmanaged resources having a constructor and a destructor.

public class Channel

{

public Channel()

{

// Create unmanaged resource

// Lock("c:\\file.log");

}

~Channel()

{

// Destroy unmanaged resource

// Unlock("c:\\file.log");

}

}
The
Channel
class locks a file "c:\file.log" in the constructor and unlocks it in the destructor.

Here arises a problem that we cannot predict when the destructor is called. So the file remains locked until the garbage collection, even if the object instance is out of any reference.

For the above problem, we need some method to free the unmanaged resource. In .NET, there is an interface named
IDisposable
for the purpose.

IDisposable

The
IDisposable
interface contains a method
Dispose()
which should be implemented by the class. So we can move freeing up of unmanaged resources to this
Dispose()
method.

Again there is a problem, that a typical .NET developer not calling the
Dispose()
method after the usage of class instance. In that case, we need the call to free unmanaged resources both in the destructor and in the
Dispose()
method.

We can also use a
Close()
method instead of implementing the
IDisposable
interface. But, enabling the
IDisposable
makes our class usable in the
using{}
statement of C#.

The
using
statement will automatically call the
Dispose()
method after exiting the scope of
using
. (Please refer to the sample code.)

public class Channel: IDisposable

{

public Channel()

{

// Create unmanaged resource

Lock("c:\\file.log");

}

~Channel()

{

// Destroy unmanaged resource

Unlock("c:\\file.log");

}

void IDisposable.Dispose()

{

Unlock("c:\\file.log");

}

public void Lock(string file) { }

public void Unlock(string file) { }

}
At first look, the above code solves all the problems.

But, it creates a new problem due to the non-deterministic destructors of C#.

Non-Deterministic Destructors

In C#, we do not know when the destructor is executed. It is called only during the garbage collection process and we do not know when it actually happens. This state is called non-deterministic destructors or InDeterministic destructors.

The following figure illustrates the problem. Our '
Channel
' class is usingsome unmanaged resources and freeing them in the
Dispose()
method and also in the destructor.

There are two cases when the
Channel
class is used:

A developer creates an instance and exits without calling
Dispose()

A developer creates an instance and calls
Dispose()




Solution usingGC.SuppressFinalize(this)

We can instruct the garbage collector not to call the destructor by usingthe
GC.SuppressFinalize(this)
method. So, using
this
in the
Dispose()
method after freeing up unmanaged resources will solve the non-deterministic
destructor problem.

The solution is given below.

Here, the
Channel
class has freed up code both in the destructor and in the
Dispose()
method. And if the developer calls the
Dispose()
method, the unmanaged resources will be freed and the destructor will be disabled
by using
GC.SuppressFinalize(this);
.

If the developer does not call the
Dispose()
method, the unmanaged resources are freed up in the destructor by the garbage collector. The figure below illustrates the solution:



Note: The
Dispose()
method is used to free up the unmanaged resources, and calling
Dispose()
will not free up the instance. It just executes whatever statements are written in it.

Using the Code

The code along with thisarticle demonstrates the case of using/not using
Dispose()
, and also the initiating of the
GarbageCollection
process using
GC.Collect()
. Please analyze the
Channel
class to see the
details involved.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: