Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Daniel Solis - Illustrated C# 2010 - 2010.pdf
Скачиваний:
16
Добавлен:
11.06.2015
Размер:
11.23 Mб
Скачать

CHAPTER 6 MORE ABOUT CLASSES

Destructors

Destructors perform actions required to clean up or release unmanaged resources after an instance of a class is no longer referenced. The important things to know about destructors are the following:

You can have only a single destructor per class.

A destructor cannot have parameters.

A destructor cannot have accessibility modifiers.

A destructor has the same name as the class but is preceded by a tilde character (pronounced

TIL-duh).

A destructor only acts on instances of classes; hence, there are no static destructors.

You cannot call a destructor explicitly in your code. Instead, it is called during the garbage collection process, when the garbage collector analyzes your code and determines that there is no longer any path through your code that references the object.

For example, the following code illustrates the syntax for a destructor of a class called Class1:

Class1

 

{

 

~Class1()

// The destructor

{

 

CleanupCode

 

}

 

...

 

}

 

Some important guidelines for using destructors are the following:

Don’t implement a destructor if you don’t need one. They can incur performance costs.

A destructor should only release external resources that the object owns.

A destructor should not access other objects because you can’t assume that those objects haven’t already been destroyed.

Note Before the release of version 3.0 of C#, destructors were sometimes called finalizers. You might sometimes still run across this term in the literature and in the .NET API method names.

140

CHAPTER 6 MORE ABOUT CLASSES

Calling the Destructor

Unlike a C++ destructor, a C# destructor is not called immediately when an instance goes out of scope. In fact, there is no way of knowing when the destructor will be called. Furthermore, as previously mentioned, you cannot explicitly call a destructor. If your code needs a destructor, you must provide it for the system, which will call it at some point before the object is removed from the managed heap.

If your code contains unmanaged resources that need to be released in a timely manner, you shouldn’t leave that task for the destructor, since there is no guarantee that the destructor will run any time soon. Instead, you should adopt the standard pattern where your classes implement what is called the IDisposable interface. (I’ll cover interfaces in Chapter 17.) This consists of encapsulating the cleanup code for these resources in a void, parameterless method, which you should call Dispose.

When you’re done with the resources and want them released, you need to call Dispose. Notice that it is you who needs to call Dispose—not the destructor. The system won’t call it for you automatically.

Some guidelines for your Dispose method are the following:

Implement the code in Dispose in such a way that it is safe for the method to be called more than once. If it has already been called, then on any subsequent invocations it should not raise an exception or do any additional work. (Exceptions are covered in Chapter 11.)

Write your Dispose method and destructor such that, if for some reason your code doesn’t get to call Dispose, your destructor will call it and release the resources.

Since Dispose is doing the cleanup rather than the destructor, it should call the

GC.SuppressFinalize method, which tells the CLR not to call this object’s destructor, because it has already been taken care of.

The following code describes the safe disposal process. First, the class needs to declare a Boolean disposed field to keep track of whether the cleanup has occurred. This is initialized to false when the object is created.

Inside the Dispose method, do the following:

Check the flag to see whether the resources have already been released. If not, then do the following:

Call the Dispose methods on any managed resources that require it.

Release any unmanaged resources held by the object.

Now that the disposal has occurred, set the disposed flag to true.

Finally, call the garbage collector’s SuppressFinalize method to tell the garbage collector not to call the class’s destructor.

141

CHAPTER 6 MORE ABOUT CLASSES

The process in the destructor is similar to but shorter than that in the Dispose method. Just check to see whether the object has already been cleaned up, and if not, then release the unmanaged resources. Notice that in this case you do not call the Dispose methods of any managed resources, because the garbage collector might have already deleted those objects.

class MyClass

 

{

 

bool disposed = false;

// Flag indicating disposal status

////////////////////////////////////////////////////////

public void Dispose()

// Public Dispose

{

 

if (disposed == false)

// Check the flag.

{

 

//Call Dispose on managed resources.

...

//Release any unmanaged resources.

...

}

 

disposed = true;

// Set the flag to show disposal.

GC.SuppressFinalize(this);

// Tell GC not to call Finalize.

}

 

////////////////////////////////////////////////////////

~MyClass()

// Destructor

{

 

if (disposed == false)

// Check the flag.

{

 

// Release any unmanaged resources.

...

}

}

...

}

142

CHAPTER 6 MORE ABOUT CLASSES

The Standard Dispose Pattern

In the previous section, you saw that the destructor code is essentially a subset of the Dispose code. The standard pattern factors out most of the common code of these two methods into another method called Dispose, which I’ll call the factored Dispose. It takes a single Boolean parameter that is used to indicate whether the method is being called from the public Dispose method (true) or from the destructor (false).

This standard dispose pattern is shown following and illustrated in Figure 6-13. I’ll cover the protected and virtual modifiers in the next chapter.

class MyClass : IDisposable

 

{

 

bool disposed = false;

// Disposal status

public void Dispose()

 

{

Public Dispose

Dispose( true );

GC.SuppressFinalize(this);

 

}

 

~MyClass()

Destructor

{

Dispose(false);

 

}

 

protected virtual void Dispose(bool disposing)

 

{

 

if (disposed == false)

 

{

 

if (disposing == true)

 

{

Factored Dispose

// Dispose the managed resources.

...

 

}

 

// Dispose the unmanaged resources.

 

...

 

}

 

disposed = true;

 

}

 

}

 

143

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]