Weak Reference


IDisposable.Dispose

The Dispose method is an alternative to a finalizer. Contrary to finalizers, Dispose methods are deterministic and can freely access managed types. Finalizers are nondeterministic, which delays invocation. This can prevent resources from being released in a timely manner and create resource contention. For example, an unreleased file handle can block or cause errors in other threads waiting on that handle. Dispose methods are deterministic and sometimes referred to as explicit garbage collection. You can call the Dispose method to release an expendable resource, such as immediately closing a file handle. Accessing managed objects in a finalizer is unadvisable. Garbage collection is undeterminable, and that object may no longer exist. This requirement greatly limits the scope and functionality of a finalizer. The Dispose method does not necessarily have these limitations because garbage collection may not be occurring.

The Dispose method is defined in the IDisposable interface of the System namespace. Disposable objects should inherit and implement the IDisposable interface, in which the Dispose method is the only member. You then call the Dispose method as a normal method to start deterministic garbage collection. Although possible, you should not implement the Dispose method separate of the IDisposable interface. The IDisposable interface is an important marker that confirms the presence of a disposable object. There are statements and behaviors, such as the using statement, that require this marker.

The following code demonstrates a simple implementation of the Dispose method:

 public class Starter{     public static void Main(){         ZClass disposableobject=new ZClass();         disposableobject.Dispose();         disposableobject=null;     } } public class ZClass: IDisposable {     public ZClass() {         // Allocate resources     }     public void Dispose() {         // Release resources     } } 

In the preceding code, the Dispose method is not guaranteed to run. Raising an exception prior to the Dispose method call could cause the method to be skipped and result in resource leakage. To protect against this possibility, place the Dispose method call in a finally block. This assures that the Dispose method is called whether or not an exception is raised in the try block. This is an updated version of the previous code, in which the Dispose method is placed in a finally block:

 public static void Main(){     ZClass disposableobject=null;     try {         disposableobject=new ZClass();     }     finally {         disposableobject.Dispose();         disposableobject=null;     } } 

The using block is the short form of the preceding code. A new object is defined in the using statement. When the using block exits, the Dispose method is called automatically on the declared object of the using statement:

 public static void Main(){     using(ZClass disposableobject=             new ZClass())     {         // use object     } } 

The C# compile substitutes a try and a finally block for the using statement and block. This is some of the MSIL code emitted for the using block:

 IL_0006:  stloc.0 .try {   IL_0007:  nop   IL_0008:  nop   IL_0009:  leave.s    IL_001b }  // end .try finally {   // partial listing…   IL_0014:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()   IL_0019:  nop   IL_001a:  endfinally }  // end handler 

Multiple objects of the same type can be declared in the using statement. Delimit the objects with commas. All objects declared in the statement are accessible in the using block. When the using block exits, the Dispose method is called on each of the declared objects from the using statement. In the following code, two objects of the same type are declared in the using statement:

 using System; namespace Donis.CSharpBook{     public class Starter{         public static void Main(){             using(ZClass obj1=new ZClass(),                          obj2=new ZClass()) {             }         }     }     public class ZClass: IDisposable {         public void Dispose() {             Console.WriteLine("ZClass.Dispose");         }     } } 

You can also declare objects of different types for a using block—simply stack multiple using statements. In the following code, three objects are scoped to the using block. One object is the XClass type, whereas two objects are ZClass types. All three are disposed at the end of the using block.

 using System; namespace Donis.CSharpBook{     public class Starter{         public static void Main(){             using(XClass obj3=new XClass())             using(ZClass obj1=new ZClass(),                          obj2=new ZClass()) {             }         }     }     public class ZClass: IDisposable {         public void Dispose() {             Console.WriteLine("ZClass.Dispose");         }     }     public class XClass: IDisposable {         public void Dispose() {             Console.WriteLine("XClass.Dispose");         }     } } 

Class can contain both a Dispose method and a finalizer. You can relinquish both managed and unmanaged resources in the Dispose method, whereas the finalizer can clean up only the unmanaged resources. When a Dispose method is not called as planned, the finalizer is an effective safety net. Finalizers are called automatically and cannot be forgotten. If not called earlier, finalizers of disposable objects are called at CLR shutdown. Finalization should not be performed on a disposed object. A second iteration of cleanup could have unexpected results. For this reason, developers typically suppress the finalizer in the Dispose method. The GC.SuppressFinalize method is called in the Dispose method to suppress the finalizer. Performance is improved because future finalization of the object is eliminated.

In the following code, the ZClass has both a Dispose and finalizer method. Note that GC.SuppressFinalize is invoked in the Dispose method.

 using System; namespace Donis.CSharpBook{     public class Starter{         public static void Main(){             using(ZClass obj1=new ZClass()) {             }         }     }     public class ZClass: IDisposable {         public void Dispose() {             Console.WriteLine("Disposing resources");             GC.SuppressFinalize(this);         }         ~ZClass() {             Console.WriteLine("ZClass.ctor");             // Cleanup unmanaged resources         }     } } 

This section reviewed the simple implementation of the Dispose method, which is sufficient for sealed classes. However, inheritable classes require the more complex Disposable pattern.

Thread Local Storage Example

Earlier in this chapter, an unworkable Thread Local Storage application was presented. The following code shows the corrected version that uses the Dispose method. For the TMonitor class, the Dispose method is called from each thread that is using the class.

 class TMonitor: IDisposable {     public void WriteFile()     {         StreamWriter sw = Thread.GetData(Form1.Slot)             as StreamWriter;         if (sw == null)         {             sw = new StreamWriter(string.Format(                 @"C:\{0}File.txt",                 Thread.CurrentThread.Name),                 true);             Thread.SetData(Form1.Slot, sw);         }         sw.WriteLine(DateTime.Now.ToLongTimeString());     }     public void Dispose()     {         StreamWriter sw = Thread.GetData(Form1.Slot)             as StreamWriter;         Thread.SetData(Form1.Slot, null);         if (sw != null)         {             sw.Close();             MessageBox.Show("sw closed");         }     } } 

Disposable Pattern

The Disposable pattern provides a template for implementing the Dispose method and finalizer in a base and derived class. The Disposable pattern, shown in the following code, should be implemented if the base class does not presently possess resources:

 using System; using System.Threading; namespace Donis.CSharpBook{     public class Base: IDisposable {         public void Dispose() {             Dispose(true);             GC.SuppressFinalize(this);         }         protected virtual void Dispose(bool disposing) {             if (disposing) {                 // Release managed resources             }             // Release unmanaged resources         }         ~Base() {             Dispose (false);         }     }     public class Derived: Base {         protected override void Dispose(bool disposing) {             if (disposing) {                 // Release managed resources.             }             // Release unmanaged resources             base.Dispose(disposing);         }     } } 

We will focus first on the base class, which implements the IDisposable interface and contains two Dispose methods.

The one-argument Dispose method, which is a protected method, has a single argument. The disposing argument indicates whether the method is being called during deterministic or nondeterministic garbage collection. If called during nondeterministic garbage collection, the disposing argument is set to false. Otherwise, the argument is true. When the argument is false, only unmanaged releases are releasable in the method. When true, both managed and unmanaged resources can be released.

Both the no-argument Dispose method and the finalizer delegate to the one-argument Dispose method. The no-argument Dispose method is public and called explicitly for deterministic garbage collection. This Dispose method delegates to the one-argument destructor with the disposing flag set to true. It also suppresses finalization of this object in future garbage collection. The finalizer delegates to the one-argument destructor with the disposing flag set to false. This limits cleanup to unmanaged resources, which is appropriate during normal garbage collection.

The no-argument Dispose method is not a virtual method, whereas the one-argument Dispose method is virtual. The no-argument Dispose method should not be overridden in the derived class. This method should always delegate to the most derived one-argument Dispose method. Any other behavior would seriously break the disposable pattern.

In the derived class, override the one-argument Dispose method to clean up managed and unmanaged resources of the derived class. Do not override or hide the no-argument Dispose method of the base class. Finally, the one-argument Dispose method in the derived class should call the same method in the base class, affording that class the opportunity to release its resources.

In the derived class, do not implement a finalizer. The base class implementation of the finalize method will correctly call the most derived dispose method. Disposal then propagates from the most derived class to all ascendants. Therefore, resource cleanup is performed in the correct order.

Disposable Pattern Considerations

There are several factors to consider when implementing a simple dispose or the more complex Disposable pattern. This section lists many of the factors that should be considered when implementing a Dispose method.

Redundant Dispose Method

In the following code, the Dispose method is called twice. Calling the Dispose method multiple times should be secure. Set a flag the first time Dispose is called. Check the flag in future invocations to confirm that the object is disposed. If the object is disposed, do not dispose it again. Alternatively, you might be able to confirm the disposability of an object from the state of the object. It is a good practice to confirm the disposed status at the beginning of other member methods. If the object is disposed, revive the object and execute the method or throw the ObjectDisposedException.

 public static void Main(){     using(ZClass disposableobject=             new ZClass())     {         disposableobject.Dispose();     } } 

The following code demonstrates a resilient Dispose method, which can be called multiple times. The ReverseReader type is a thin wrapper for a StreamReader. It inverts information read from the StreamReader. ReserveReader contains a StreamReader field. It is initialized in the class constructor and closed in the Dispose method. If the StreamReader is null, the object is presumed disposed. This is checked in the Dispose method. If the object is already disposed, the Dispose method simply returns. When the object is disposed, the other ReverseReader method throws the ObjectDisposedException exception. After the using block, a second Dispose method is called, which proves to be harmless. A second call to the ReadLine method is commented. If uncommented, an exception would be raised.

 using System; using System.IO; namespace Donis.CSharpBook{     public class Starter{         public static void Main(){             using(ReverseReader input=new                     ReverseReader("text.txt")) {                 string result=input.ReadLine();                 while(result!=null) {                     Console.WriteLine(result);                     result=input.ReadLine();                 }                 input.Dispose();                 // input.ReadLine();             }         }     }     public class ReverseReader:IDisposable {         public ReverseReader(string filename) {             file=new StreamReader(filename);         }         public string ReadLine() {             if(file==null) {             throw new ObjectDisposedException(                 "ReadLine object");         }         if(file.Peek()<0) {             return null;         }         string temp=file.ReadLine();         char [] tempArray=temp.ToCharArray();         Array.Reverse(tempArray);         return new string(tempArray);     }     public void Dispose() {        if(file==null) {            return;        }        else {            file.Close();            file=null;        }     }     private StreamReader file=null; } 

Close Method

Instead of the Dispose method, some classes expose another method for deterministic cleanup. Although this should not be done as a general practice, the exception is when another method is more intuitive than the Dispose method. For example, the FileStream class exposes the Close method. (Close is the traditional term for releasing a file.) The alternative method should delegate to the Dispose method. Do not implement the disposable routine more than once. Both the Dispose and alternative methods are available to the clients for deterministic garbage collection. The correct implementation is demonstrated with the StreamWriter.Close method, as shown in the following code. StreamWriter.Close delegates to TextWriter.Dispose for deterministic garbage collection. TextWriter.Dispose is inherited by the StreamWriter class. The Close method suppresses the finalizer, which is standard behavior of a deterministic method. Both the Close and Dispose method are available on the StreamWriter class. You should clearly document any alternate method for deterministic garbage collection.

 .method public hidebysig virtual instance void         Close() cil managed {   // Code size       14 (0xe)   .maxstack  8   IL_0000:  ldarg.0   IL_0001:  ldc.i4.1   IL_0002:  callvirt   instance void System.IO.TextWriter::Dispose(bool)   IL_0007:  ldarg.0   IL_0008:  call       void System.GC::SuppressFinalize(object)   IL_000d:  ret } // end of method StreamWriter::Close 

Thread-Safe Dispose Method

The Dispose method is not implicitly thread-safe. As a public method, Dispose is callable for multiple threads simultaneously. Thread synchronization is required for thread-safeness. The lock statement, as demonstrated in the following code, is the convenient means of supporting simultaneous access to the Dispose method.

 public class ZClass: IDisposable {     public void Dispose() {         lock(this) {         }     } } 

Reusable Objects

The Disposable pattern accommodates reusable objects. Unless the object is nondisposed, it can be recycled. Do not recycle an object implicitly. Automatically recycling an object on the assignment operator or a function call is not recommended. Expose a method that explicitly recycles the object. There is no convention for naming this method. However, an Open method is always a good choice. This method should have the dual purpose of initializing instances created with a default constructor. Recyclable objects should also expose a property that confirms the status of the object as alive or disposed.

The following code is a revision of the ReverseReader class, which was presented earlier in this chapter. This version is recyclable. It has a default and one-argument constructor. The one-argument constructor delegates to the Open method. You can call the Open method to initialize or recycle a ReverseReader instance. The Active property returns the status of the object. If true, the object is active.

 public class ReverseReader:IDisposable {     public ReverseReader() {     }     public ReverseReader(string filename) {         Open(filename);     }     public bool Open(string filename) {         if(file!=null) {             return false;         }         file=new StreamReader(filename);         return true;     }     public string ReadLine() {         if(file==null) {             throw new ObjectDisposedException(                 "ReadLine object");         }         if(file.Peek()<0) {             return null;         }         string temp=file.ReadLine();         char [] tempArray=temp.ToCharArray();         Array.Reverse(tempArray);         return new string(tempArray);     }     public void Dispose() {         if(file==null) {             return;         }         else {             file.Close();             file=null;         }     }     public void Close() {         Dispose();     }     private StreamReader file=null;     public bool Active {         get {             return !(file==null);         }     } } 

Disposing Inner Objects

A disposable class should dispose the disposable fields contained in the type. Call the Dispose method of those fields in the Dispose method of the class. After disposing, set the disposable fields to null. Of course, the inner objects dispose the disposable objects they contain. In this way, the Dispose method is transitive.

The proper disposal of inner objects is shown in the following code:

 public class ZClass: IDisposable{     public ZClass() {         inner=new YClass();     }     public void Dispose() {         Console.WriteLine("ZClass.Dispose");         inner.Dispose();         inner=null;     }     private YClass inner=null; } public class YClass: IDisposable{     public void Dispose() {         Console.WriteLine("YClass.Dispose");     } } 

An object should not dispose any object not fully within its control, which can cause some unwanted side affects. In the following code, the _inner field is initialized with a property. The _inner field is a disposable type. Two instances of the containing class are created. One is created before the using block, and the other is created in the using statement. The _inner field of both objects is initialized to the same instance. Therefore, neither object has full control of the object. When the using block is exited, the inner object is disposed in the second object. However, the other object remains active. When the remaining active object attempts to access the inner object, an exception is raised.

 using System; namespace Donis.CSharpBook{     public class Starter{         public static void Main(){             ZClass obj1=new ZClass();             obj1.Inner=new YClass();             using(ZClass obj2=new ZClass()) {                 obj2.Inner=obj1.Inner;             }             obj1.MethodA(); // exception             obj1.Dispose();             obj1=null;         }     }     class ZClass: IDisposable{         public ZClass() {         }         public void Dispose() {             Console.WriteLine("ZClass.Dispose");             _inner.Dispose();         }         public void MethodA() {             Console.WriteLine("ZClass.MethodA");             _inner.MethodA();         }         public YClass Inner {             set {                 _inner=value;             }             get {                 return _inner;             }         }         private YClass _inner=null;     }     class YClass: IDisposable{         public void Dispose() {             Console.WriteLine("YClass.Dispose");             disposed=true;         }         public void MethodA() {             if(disposed) {                 throw new ObjectDisposedException(                     "YClass disposed");             }             Console.WriteLine("YClass.MethodA");         }         private bool disposed=false;     } } 




Programming Microsoft Visual C# 2005(c) The Language
Microsoft Visual Basic 2005 BASICS
ISBN: 0619267208
EAN: 2147483647
Year: 2007
Pages: 161

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net