Tech Point Fundamentals

Thursday, January 14, 2021

IDisposable Interface in C# | Using-Dispose Pattern | Dispose Method

IDisposable Interface in C# | Using-Dispose Pattern C# | Dispose Method

IDisposable-Interface-And-UsingStatements-InCSharp

  In the previous article, we see what are the issues of using the finalizer or destructor in C#. In this article, we see how we can overcome the issues of the finalizer. Please read our previous article of finalizer before continuing this article.





Introduction

Finalizers are used to release unmanaged resources in C#, and destructors are invoked by the Garbage Collector and Garbage Collection is non-deterministic in C#. So to ensure the deterministic release of resources for instances of the class we have to use the Dispose() method or using-dispose pattern.

Please read about the finalizer or destructor here.



IDisposable Interface

IDisposable is an interface that contains only a single method i.e. Dispose(), for releasing unmanaged resources. IDisposable is defined in the System namespace. It provides a mechanism for releasing unmanaged resources. When your application or class library encapsulates unmanaged resources such as files, fonts, streams, database connections, etc, they should implement the IDisposable interface or the IAsyncDisposable interface.

Implementing the IDisposable interface only forces the implementation of the Dispose() method for any class. Inside the Dispose() method we can perform the resources clean-up and also call GC.SuppressFinalize(this) to mark our object as finalized. 

We can also use the Dispose() method to explicitly release unmanaged resources in conjunction with the garbage collector. The consumer of an object can call this method explicitly when the object is no longer needed. If the client forgets to call the Dispose() method the finalizer calls it automatically at the end.



IDisposabe Interface Fundamental Points

    1. You should implement IDisposable only if your class uses unmanaged resources directly.
    2. It is a breaking change to add the IDisposable interface to an existing class because pre-existing clients of the class cannot call Dispose(), so you cannot be certain that unmanaged resources held by your class will be released.
    3. Since the Dispose() method is called by the client explicitly when the resources owned by an instance are no longer needed, you should either wrap the managed object in a SafeHandle or you should override Object.Finalize() to free unmanaged resources in the event that the consumer forgets to call Dispose() method.
    4. If you are using a class that implements the IDisposable interface, you should call its Dispose() method when you are finished using the class by try/finally block or using statement.
    5. If an object's Dispose() method is called more than once, the object must ignore all calls after the first one and it must not throw any exception. Instance methods other than Dispose() can throw an ObjectDisposedException when resources are already disposed.



IDisposabe Interface Fundamental Rules

    1. An abstract class can implement the IDisposable interface but a static class cannot implement the IDisposable interface.
    2. The class should not implement IDisposable explicitly, e.g. the Dispose() method should be public. Please watch explicit interface implementation here.
    3. The class should always contain the protected virtual void Dispose(bool) method because this method allows the derived classes to correctly dispose the resources of this class.
    4. The code or content of the Dispose() method should be only the invocation of Dispose(true) followed by GC.SuppressFinalize(this).
    5. If the class has any finalizer, i.e. a destructor, the only code in its body should be a single invocation of Dispose(false).
    6. If a base class implements IDisposable, any derived class should not have IDisposable in the list of its interfaces. In such cases, it is recommended to override the base class's protected virtual void Dispose(bool) method or its equivalent.
    7. If the class inherits from a class that implements IDisposable it must call the Dispose(), or Dispose(bool) method of the base class from within its own implementation of Dispose() or Dispose(bool), respectively. This ensures that all resources from the base class are properly released.
    8. Unlike destructor or finalizer, the Dispose() method is called in the same order as the constructor i.e base class Dispose() method is always called before the derived class's Dispose() method.
    9.  A structure can contain constructors but not destructors. A structure can extend from an IDisposable interface but cannot implement it properly because a structure can not define any virtual member or instance member like a class. Since structures are value types in C#, there is no need to have any finalizer or IDisposable.





Example


 



Garbage Collector

The .NET's garbage collector manages the allocation and de-allocation of memory for our application. Every time when we create a new object, the common language runtime allocates memory for the object from the managed heap. 

GC keeps track of all the objects and ensures that each object gets destroyed once. It ensures that objects, which are being referenced, are not destroyed. GC destroys the objects only when necessary. 

The garbage collector's optimizing engine determines the best time to perform a collection, based upon the allocations being made.  Garbage collector checks for objects that are no longer being used by the application, if it found an object eligible for destruction, it calls the destruction and reclaims the memory used to store the object.



GC.Collect():

It forces an immediate garbage collection of all generations. It also freezes the main thread and associated child threads of the application. 

When the GC.Collect() method is called, the runtime performs a blocking garbage collection of all generations. All objects, regardless of how long they have been in memory, are considered for collection; however, objects that are referenced in managed code are not collected. 

It is always recommended not to use GC.Collect() unless there is a specific reason to use it. A GC typically consists of two phases i.e. Mark and Sweep phases followed by a Compaction phase



GC.SuppressFinalize(object):

This method requests that the CLR not call the finalizer for the specified object. The GC.SuppressFinalize() method tells GC that the object has been manually disposed and no more finalization is necessary for the specified object. As soon as GC.SuppressFinalize() method called, the object reference is removed from the finalization/freachable queue. The goal is to prevent finalizing the object twice.

Objects that implement the IDisposable interface can call this method from the object's Dispose() implementation to prevent the garbage collector from calling Object.Finalize() on an object that does not require it. 

Usually, this is done to prevent the finalizer from releasing unmanaged resources that have already been freed by the Dispose() implementation. But if obj does not have a finalizer or the garbage collector has already signaled the finalizer thread to run the finalizer, the call to the SuppressFinalize method has no effect.



GC.ReRegisterForFinalize(object):

By default, all objects that implement finalizers are added to the list of objects that require finalization; however, an object might have already been finalized or might have disabled finalization by calling the SuppressFinalize method.

The ReRegisterForFinalize() method requests that the system call the finalizer for the specified object for which SuppressFinalize() has previously been called. This method adds the specified obj to the list of objects that request finalization before the garbage collector frees the object. 

However, calling the ReRegisterForFinalize() method does not guarantee that the garbage collector will call an object's finalizer. But a finalizer can use this method to resurrect itself or an object that it references.



Try/Finally Block Dispose Pattern

You can call the IDisposable.Dispose() method from the finally block of a try/finally statement. By using the finally block, you can clean up any resources that are allocated in a try block, and you can run code even if an exception occurs in the try block. 

Normally, the statements of a finally block run when control leaves a try statement or a catch statement in case of any exception. Within a handled exception, the associated finally block is guaranteed to be run. 

However, if the exception is unhandled, execution of the finally block is dependent on how the exception unwind operation is triggered. If you have statements in a finally block that must be run even in that situation, one solution is to add a catch block to the try-finally statement. 



Using Statement  Dispose Pattern

The using statement defines a scope at the end of which an object will be disposed. It provides a convenient syntax that ensures the correct use of IDisposable objects.

When the lifetime of an IDisposable object is limited to a single method, you should declare and instantiate it in the using statement. The using statement calls the Dispose() method on the object in the correct way and it also causes the object itself to go out of scope as soon as Dispose() is called. 



The using statement ensures that Dispose() method is called on the object created within it. The compiler translates the using statement into a try-finally block internally. In the finally part Dispose() method is called on the object’s instance. This way the compiler ensures that even if there’s any exception thrown in the using block, the object will be disposed. 

Using-Dispose-Pattern-ILCode

Within the using block, the object is read-only and can't be modified or reassigned. The using statement ensures that Dispose (or DisposeAsync) is called even if an exception occurs within the using block. 

Multiple instances of a type can be declared in a single using statement by using a comma separator. But you cannot use implicitly typed variables (var) when you declare multiple variables in a single statement.

You can also instantiate the resource object first and then pass the variable to the using statement, but this is not a best practice because in this case, after control leaves the using block, the object remains in scope but probably has no access to its unmanaged resources. So it is not fully initialized anymore, if you try to use the object outside the using block, you risk causing an exception to be thrown.



Real Example of IDisposable Classes 

    1. System.IO.Stream abstract class implements IDisposable.
    2. System.IO.FileStream class contains the destructor along with the overriding the Dispose method of the base Stream class.
    3. System.Data.Common.DbConnection abstract class implements the IDisposable.
    4. System.Data.SqlClient.SqlConnection sealed class overrides the Dispose(bool disposing) method of DbConnection base class.
    5. System.IO.TextWriter abstract class implements IDisposable.
    6. System.IO.TextReader abstract class implements IDisposable.
    7. System.Net.Http.HttpMessageInvoker class implements IDisposable.
    8. System.Net.Http.HttpClient class overrides the Dispose(bool disposing) method of HttpMessageInvoker base class.
    9. System.ComponentModel.Component class implements IDisposable.
    10. System.Net.WebClient class inherits from base class Component which has implemented the IDisposable interface.



Live Demo





Conclusion

Unlike finalizer, implement IDisposable interface and use Dispose() method to explicitly release unmanaged resources whenever required. You can also use it along with the finalizer. You can either use try/finally block or the using statement to dispose the object.

No comments:

Post a Comment

Please do not enter any HTML. JavaScript or spam link in the comment box.