代码改变世界

Explicitly Release Resources In C#

2012-05-23 15:28  libiver  阅读(853)  评论(0编辑  收藏  举报

声明:欢迎任何人和组织转载本blog中文章,但必须标记文章原始链接和作者信息。

本文链接:http://www.cnblogs.com/leezhm/archive/2012/05/23/2514862.html

开拓进取的小乌龟------->cnBlogs 点滴点点滴滴 Blog

        

           According the last blog(Here), destructor cannot be called by programmers , it is invoked automatically by GC in a uncertain time.So whether it means programmers  need not  care about releasing resource(or GC) in C#.

           In general, C# do not require as much memory management as is need.This is because the .NET Framework Garbage Collector implicitly manages the allocation and release of memory for your application.However most time, a explicit and accuracy way that programmers can release memory by themselves  is necessary.For example, when your application encapsulate unmanaged resources such as a window, file, font, database connection, network connection, and so on, you will find it very useful and helpful.So how many different and explicit ways can clean up the rare and expensive external resources that using by your application?

           Three ways provided on MSDN,for more details about them, see the following parts.

  • Implementing a Dispose Method

           The IDispose Interface provide Dispose Method,it is used only for unmanaged resource. Do not implement and call it, unless application has encapsulate some unmanaged resources.For all managed resources, the Garbage Collector has a very efficient and implicit way to manage them,please never do the idle work.

           A class’s Dispose method should release all resources that it owns and it should also clean up all resources owned by its parent class by calling the parent’s Dispose method,and then, the parent will continue to invoke its base’s Dispose method.To help ensure all resources are always released completely, a Dispose method should be called several times without throwing any exceptions.Do not forget to call SuppressFinalize function in the Dispose,it will prevents the Finalize method from being invoked if current object is on the finalization queue.For more details about Finalize method will be described in the next title, and please remember that Finalize method called by GC automatically and executing a Finalize method is  costly to performance.If a Dispose method was called for release resources,it is not necessary for the Garbage Collector to called the disposed object’s Finalize again.

            The following example shows how to implement a Dispose method is a good pattern.

   1:  namespace CSnippets.GC
   2:  {
   3:      using System;
   4:      using System.Collections.Generic;
   5:      using System.Linq;
   6:      using System.Text;
   7:      using System.IO;
   8:   
   9:      public class DisposeExample
  10:      {
  11:          public static void TestDispose()
  12:          {
  13:              try
  14:              {
  15:                  string path = Environment.CurrentDirectory + "\\App.config";
  16:   
  17:                  FileStream fs = File.OpenRead(path);
  18:   
  19:                  DisposableResource dr = new DisposableResource(fs);
  20:                  
  21:                  //
  22:                  dr.DoSomethingWithResource();
  23:   
  24:                  //
  25:                  dr.Dispose();
  26:              }
  27:              catch (FileNotFoundException expt)
  28:              {
  29:                  Console.WriteLine(expt.Message);
  30:              }
  31:          }
  32:      }
  33:   
  34:      public class DisposableResource : IDisposable
  35:      {
  36:          private Stream resource = null;
  37:          private bool disposed = false;
  38:   
  39:          public DisposableResource(Stream stream)
  40:          {
  41:              if (null == stream)
  42:              {
  43:                  throw new ArgumentNullException("stream is null ...");
  44:              }
  45:   
  46:              if (!stream.CanRead)
  47:              {
  48:                  throw new ArgumentException("stream must be readable ...");
  49:              }
  50:   
  51:              resource = stream;
  52:              disposed = false;
  53:          }
  54:   
  55:          public void DoSomethingWithResource()
  56:          {
  57:              if (disposed)
  58:              {
  59:                  throw new ObjectDisposedException("resource was disposed ...");
  60:              }
  61:   
  62:              int num = (int)resource.Length;
  63:              Console.WriteLine("Number of bytes: {0}", num.ToString());
  64:          }
  65:   
  66:          public void Dispose()
  67:          {
  68:              Dispose(true);
  69:   
  70:              //  Using SuppressFinalize in case a subclass
  71:              //  of this type implements a finalizer.
  72:              System.GC.SuppressFinalize(this);
  73:          }
  74:   
  75:          protected virtual void Dispose(bool disposing)
  76:          {
  77:              // If you need thread safety, use a lock around these 
  78:              // operations, as well as in your methods that use the resource.
  79:              if (!disposed)
  80:              {
  81:                  if (disposing)
  82:                  {
  83:                      // Release unmanaged resource
  84:   
  85:                      if (null != resource)
  86:                      {
  87:                          resource.Dispose();
  88:                          Console.WriteLine("Object disposed ...");
  89:                      }
  90:                  }
  91:   
  92:                  // Release managed resource
  93:   
  94:                  // Indicate that the instance has been disposed.
  95:                  resource = null;
  96:                  disposed = true;
  97:              }
  98:          }
  99:      }
 100:  }
 

         Actually,sometimes Close method is also used to release resources such as FileStream.Close.

  • Overriding the Finalize Method

         Finalize is a protected method provided by the base class Object, so implement the Finalize method in C#, you must use destructor syntax.It is the same as Dispose method, a Finalize method should release all resources its owned and its parent owned by calling its base’s Finalize method.

         By default, the Object.Finalize method does nothing,unless override it by yourself.For example, if you want to clean up all resources before system executes the GC, you must override it in your class.and more important thing, you must ensure that a Finalize method should not throw any exceptions, because the application can not handle it and it can cause the application to terminate.

         Example is the best documents for programmer, is it right?Please see the below code snippets.

   1:  // Design pattern for a base class.
   2:  public class Base: IDisposable
   3:  {
   4:      private bool disposed = false;
   5:   
   6:      //Implement IDisposable.
   7:      public void Dispose()
   8:      {
   9:          Dispose(true);
  10:          GC.SuppressFinalize(this);
  11:      }
  12:   
  13:      protected virtual void Dispose(bool disposing)
  14:      {
  15:          if (!disposed)
  16:          {
  17:              if (disposing)
  18:              {
  19:                  // Free other state (managed objects).
  20:              }
  21:              // Free your own state (unmanaged objects).
  22:              // Set large fields to null.
  23:              disposed = true;
  24:          }
  25:      }
  26:   
  27:      // Use C# destructor syntax for finalization code.
  28:      ~Base()
  29:      {
  30:          // Simply call Dispose(false).
  31:          Dispose (false);
  32:      }
  33:  }
  34:   
  35:  // Design pattern for a derived class.
  36:  public class Derived: Base
  37:  {
  38:      private bool disposed = false;
  39:   
  40:      protected override void Dispose(bool disposing)
  41:      {
  42:          if (!disposed)
  43:          {
  44:              if (disposing)
  45:              {
  46:                  // Release managed resources.
  47:              }
  48:              // Release unmanaged resources.
  49:              // Set large fields to null.
  50:             // Call Dispose on your base class.
  51:              disposed = true;
  52:          }
  53:          base.Dispose(disposing);
  54:      }
  55:      // The derived class does not have a Finalize method
  56:      // or a Dispose method without parameters because it inherits
  57:      // them from the base class.
  58:  }

         Aha, it looks same as the topic how to implement a Dispose method except the yellow part.Why does it need to do like that?

         Actually, Even when you provide explicit calling Dispose method, you should invoke the Finalize method implicitly.The Finalize provide a backup to prevent resources from permanently leaking if the programmer fails to call Dispose method, even executing a Finalize method is an expensive operation.

         So what is your opinion?Do you think it is one way and one completely way that do those two ways together to release resources?

  • Using the using statement

        Using statement provide a simple and convenient syntax to ensure that a correct way is be used for a IDispose object.It can make sure that Dispose method is called even if an exception occurs.For example, for StreamWriter, the using statement automatically closes the stream and invokes Dispose method on the StreamWriter object at the end of using statement.

        Below is a good practice that shows how to use using statement to clean up resources with Font(System.Drawing.Font) object.

   1:  using (Font ft = new Font("Arial", 15.0f))
   2:  {
   3:      byte charset = ft.GdiCharSet;
   4:   
   5:      Console.WriteLine("Charset of Arial is {0}", charset);
   6:  }
   7:   
   8:  Font ft2 = new Font("Arial", 10.0f);
   9:  using (ft2) // not recommended
  10:  {
  11:      // use font2
  12:      byte charset2 = ft2.GdiCharSet;
  13:   
  14:      Console.WriteLine("Charset 2 of Arial is {0}", charset2);
  15:  }
  16:  // font2 is still in scope
  17:  // but the method call throws an exception
  18:  float f = ft2.GetHeight(); 

        Of course, it is a much more complex topic of Garbage Collection in C# and there is several times contents about GC on MSDN than here.More details and comprehensive contents about Garbage Collection,see this webpage.have fun!