Case Analysis of C Resource Release Method

  • 2021-09-11 21:00:57
  • OfStack

This article illustrates the C # resource release method. Share it for your reference, as follows:

1. try {} finally {}

2. using

Only types that implement the IDisposable interface and override the Dispose () method can use the using statement to implement resource release.

First, let's look at the description of this interface in MSDN:


[ComVisible(true)]
public interface IDisposable
{ // Methods
void Dispose();
}

1. [ComVisible (true)]:

Indicates that the managed type is visible to COM.

2. The primary purpose of this interface is to release unmanaged resources.

When a managed object is no longer used, the garbage collector automatically frees the memory allocated to the object. However, it is impossible to predict when garbage collection will take place. In addition, the garbage collector has no knowledge of unmanaged resource 1, such as window handles or open files and streams. Use the Dispose method of this interface with garbage collector 1 to explicitly release unmanaged resources. When the object is no longer needed, the consumer of the object can call this method.

1. Basic application

1. Let's define a class that implements the IDisposable interface. The code is as follows:


public class TestClass :IDisposable
{
  public void DoSomething()
  {
    Console.WriteLine("Do some thing....");
  }
  public void Dispose()
  {
    Console.WriteLine(" Release resources in time ");
  }
}

2. We have two ways to call:

2.1. In the first way, using the Using statement automatically calls the Dispose method with the following code:


using (TestClass testClass = new TestClass())
{
  testClass.DoSomething();
}

2.2 In the second way, the Dispose method of the interface is actually called, and the code is as follows:


TestClass testClass = new TestClass();
try {
  testClass.DoSomething();
}
finally
{
  IDisposable disposable = testClass as IDisposable;
  if (disposable != null)
  disposable.Dispose();
}

The execution results of the two methods are the same.

2.3. The advantage of using try/catch/finally is that the exception can be handled after being caught and the resource can be released at the same time; However, using using, there are exceptions can also release resources, but can not handle exceptions, directly release exceptions, but in fact, these two methods on the release of resources is a sample.

2. Disposable mode

1. In. NET, because the Finalize method is automatically called when the object becomes inaccessible, the Dispose method that we manually call the IDisposable interface is very similar to the method called by the object finalizer, so we'd better put them in 1 for processing.

Our first thought is to override the Finalize method, as follows:


protected override void Finalize()
{
  Console.WritleLine(" Destructor execution ...");
}

When we compile this code, we find that the compiler will report the following error: This is because the compiler completely shields the Finalize method of the parent class, and the compiler prompts us to provide a destructor instead if we want to override the Finalize method, so we will provide a destructor below:

~ TestClass () {Console. WriteLine ("Destructor execution...");}

In fact, the destructor compiler will turn it into the following code:


protected override void Finalize()
{
  try {
    Console.WritleLine(" Destructor execution ...");
  }
  finally {
    base.Finalize();
  }
}

2. We can then process the Dispose method call and the object finalizer in 1, as follows:


public class TestClass: IDisposable
{
  ~TestClass()
  {
    Dispose();
  }
  public void Dispose()
  { //  Clean up resources 
  }
}

3. The above implementation actually calls the Dispose method and the Finalize method, which may lead to repeated cleanup work, so there is the following classic Disposable pattern:


private bool _isDisposed = false;
public void Dispose()
{
  Dispose(true);
  GC.SupressFinalize(this);
}
protected void Dispose(bool Diposing)
{
  if(!_isDisposed)
  {
    if(Disposing)
    {
      // Clean up managed resources 
    }
    // Clean up unmanaged resources 
  }
  _isDisposed=true;
}
~TestClass()
{
  Dispose(false);
}

3.1. SupressFinalize method to prevent the garbage collector from calling Object. Finalize () on objects that do not need to be terminated.

3.2. With the IDisposable. Dispose method, users can release resources at any time before objects can be collected as garbage. If the IDisposable. Dispose method is called, this method releases the object's resources. In this way, there is no need to terminate. IDisposable. Dispose should call GC. SuppressFinalize so that the garbage collector does not call the finalizer of the object.

3.3. We don't want the Dispose (bool Diposing) method to be called externally, so its access level is protected. If Diposing is true, managed and unmanaged resources are released. If Diposing is equal to false, the method has been called by the runtime from within the finalizer and only unmanaged resources can be released.

3.4. ObjectDisposedException may be thrown if another method is called after the object is released.

3. Example parsing

1. The following code encapsulates the Dispose method to show how to implement a general example of Dispose (bool) in a class that uses managed and native resources:


public class BaseResource : IDisposable
{
  //  Unmanaged resource 
  private IntPtr _handle;
  // Managed resources 
  private Component _components;
  // Dispose Is called 
  private bool _disposed = false;
  public BaseResource() { }
  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }
  protected virtual void Dispose(bool disposing)
  {
    if (!this._disposed)
    {
      if (disposing)
      {
        //  Release managed resources 
        _components.Dispose();
      }
      //  Release unmanaged resources if disposing For false,  Only unmanaged resources are released 
      CloseHandle(_handle);
      _handle = IntPtr.Zero;
      //  Note that this is not thread-safe 
    }
    _disposed = true;
  }
  //  The destructor will only be used when we do not directly call the Dispose Method is called when 
  //  Derived classes do not need to provide destructors at the second time 
  ~BaseResource() { Dispose(false); }
  //  If you have called Dispose Method before calling another method throws a ObjectDisposedException
  public void DoSomething()
  {
    if (this._disposed)
    {
      throw new ObjectDisposedException();
    }
  }
}
public class MyResourceWrapper : BaseResource
{
  //  Managed resources 
  private ManagedResource _addedManaged;
  //  Unmanaged resource 
  private NativeResource _addedNative;
  private bool _disposed = false;
  public MyResourceWrapper() { }
  protected override void Dispose(bool disposing)
  {
    if (!this._disposed)
    {
      try
      {
        if (disposing)
        {
          _addedManaged.Dispose();
        }
        CloseHandle(_addedNative);
        this._disposed = true;
      }
      finally
      {
        base.Dispose(disposing);
      }
    }
  }
}

2. With the CLR garbage collector, you no longer have to worry about managing the memory allocated to the managed heap, but you still need to clean up other types of resources. Managed classes use the IDisposable interface to enable their consumers to release potentially important resources before the garbage collector finalizes the object. By following the disposable pattern and being mindful of problems, a class can ensure that all its resources are properly cleaned and that no problems occur when cleaning code is run directly through an Dispose call or through a finalizer thread.

I hope this article is helpful to everyone's C # programming.


Related articles: