Valid tive C # item18: implement the standard dispose Pattern

Source: Internet
Author: User

Valid tive C # item18: implement the standard dispose Pattern

Releasing unmanaged resources is an important part. For custom types, if resources need to be released, we should use the standard mode of. NET Framework to release unmanaged resources. In standard mode, the idisposable interface is used to explicitly release resources, and the finalizer is used to release resources when users forget to release resources. Whether the object Terminator should be called is determined by GC (Garbage Collector. This is the correct way to handle unmanaged resources.

The basic type of the custom type should implement the idisposable interface to release resources, and we should also add a protective Terminator. When our Derived classes have their own resources that need to be released, we can rewrite them to release resources properly.

First, if our class uses unmanaged resources, there must be a Terminator. We cannot expect the user program to call the dispose () method every time. Although the waste of resources is because they did not call dispose, we can solve this problem by creating a Terminator.

When GC starts to work, it first removes spam objects without a terminator from the memory, and adds all objects with a terminator to a spam queue. GC calls a new county to execute the terminator of these objects. After the Terminator is executed, the object will be removed from the queue. Objects with Terminator will be retained for a longer period of time than those without in the memory. This is not what we want, but to improve protection, we must provide a terminator for types that use unmanaged resources. However, we don't have to worry that this will reduce performance, because we can easily avoid this consumption in the next step.

The standard method for notifying users and systems to release resources is to implement the idisposable interface. This interface only contains one method:

Public interface idisposable
{
Void dispose ();
}

When we implement this method, we should do four tasks:

1. Release all unmanaged Resources

2. Release all managed resources (including undo events)

3. Set the tag to record whether the object is released. We should check the settings before releasing resources. If you call dispose for an object that has been released, an objectdisposed exception should be thrown.

4. Disable the Terminator. Call the GC. suppressfinalize (this) method to disable the operation of the Terminator on this object.

Through the implementation of the idisposable interface, we have completed two tasks: providing a mechanism for the customer program to release managed resources in a timely manner and a standard method to release unmanaged resources. This is very important. The client can avoid resource consumption by calling the Terminator.

However, our mechanism has other problems. If the derived class causes the base class to release its resources when releasing resources? If the derived class overrides the Finalize method or implements its own idisposable interface, these methods must call the base class. Otherwise, the base class resources cannot be properly released. At the same time, finalize and dispose share the same responsibilities. Generally, most of their code is the same. There is also a third method in the standard resource release mode, which is a protected virtual auxiliary function. It integrates the preceding tasks by adding a resource release hook to the derived class. The base class contains the code of the core interface. This virtual method provides a hook for the derived class to clean up resources to respond to the dispose or Terminator:

Protected virtual void dispose (bool isdisposing)

This overload method supports the Terminator and the necessary dispose methods, and because it is virtual, it can also provide an entry point for the derived class. A derived class can override this method to release resources and call its base class method to release base class resources. When isdisposing is true, managed and unmanaged resources are cleared. When it is false, we clean up unmanaged resources. After that, call the dispoe (bool) of the base class to clear the base class resources.

The following is a simple example to implement the resource release mode. The myresourcehog class implements the idispose interface, Terminator, and creates a virtual method dispose.

Public class myresourcehog: idisposable
{
Private bool _ alreadydisposed = false;

// Finalizer
~ Myresourcehog ()
{
Dispose (false );
}

// Virtual dispose
Protected virtual void dispose (bool isdisposing)
{
If (_ alreadydisposed)
{
Return;
}
If (isdisposing)
{
// Release managed resources
}
// Release unmanaged Resources
_ Alreadydisposed = true;
}

Idisposable member # region idisposable Member
Public void dispose ()
{
Dispose (true );
GC. suppressfinalize (this );
}
# Endregion

}

If the derived class has additional resources to be released, rewrite the protected dispose method:

Public class derivedresourcehog: myresourcehog
{
Private bool _ disposed = false;

Protected override void dispose (bool isdisposing)
{
If (_ disposed)
{
Return;
}
If (isdisposing)
{
// Release managed resources
}
// Release unmanaged Resources
Base. Dispose (isdisposing );
_ Disposed = true;
}
}

It should be noted that both the base class and the derived class have a member variable to mark whether the object has been released. This is a protection consideration. With this tag, you can locate an exception to a specific class, rather than all the classes contained in the object.

We need to write dispose and terminator for protection. The order in which resources are released may be arbitrary. In some cases, we have not called dispose, but the object has been cleared. We should not regard it as an error of repeatedly calling dispose. When an object has been dispose, the next call will not perform any operation. The Terminator follows the same rule. At this time, our objects are still in the memory, so we do not have to check whether they are null. However, they may have been released or terminated.

This provides an important suggestion for object cleanup: All we need to do is release resources. Do not perform other operations in these methods, which will cause serious problems. The object should be "Born" at creation, and "died" after the GC recovers the resource ". For these released resources, their statuses are similar to "Sleep ". We should regard these objects as inaccessible. If an object is inaccessible, we cannot call any of its methods. In this sense, it is "dead. However, after the object calls its terminator, there is still a sigh of relief. The Terminator should not be used to clean up resources. If some operations on a terminator make an object accessible again, we call it "resurrected ). The following is an example:

Public class badclass
{
Private readonly arraylist _ finalizedlist;
Private string _ MSG;
Public badclass (arraylist badlist, string MSG)
{
_ Finalizedlist = badlist;
_ MSG = (string) msg. Clone ();
}

~ Badclass ()
{
// The released object can be reached through list
_ Finalizedlist. Add (this );
}
}

When the badclass object calls the Terminator, it places its reference in a global table. This makes it reachable and revived. The problems brought by this will make everyone afraid. The object has been terminated, so GC does not think it is necessary to call its terminator again. If we need to end a resurrection object again, it will not happen. In addition, some of our resources become unavailable. GC does not remove objects that can only be reached in the end queue from the memory, but they may have been terminated. If this is done, they cannot be used any more. Although badclass members are still in the memory, they seem to have been released. In C #, we cannot control the order of termination. We cannot make it more reliable. Do not try this way.

I have never seen such a code that clearly uses the Resurrection object, except for the exercises at school. However, some code tries to do some real work in the Terminator. When some functions that save their own references are called, the object is revived. In theory, we should be very careful with the Terminator and dispose. If we really need to do some other work, we should pay attention to whether these actions will cause problems. Do not perform any operations unrelated to resource release.

In a hosted environment, we do not need to create a terminator for each custom type. We only release resources for types that store unmanaged resources or contain members that implement the idisposable interface. Even if we only need the disposable interface, instead of the Terminator, we should implement the entire mode.

Translated from Objective C #: 50 specific ways to improve your C # by Bill Wagner

Back to directory

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.