.NET: Handle Unmanaged Resources
Introduction
When the garbage collector runs it can clean up your managed resources. The garbage collector does not know how to free unmanaged resources (such as file handles, network connections and database connections).
The following are two mechanisms to automate the freeing of unmanaged resources:
- Declaring a destructor (or Finalizer) as a member of your class.
- Implementing the System.IDisposable interface in your class.
Destructors
Destructors are called before an object is destroyed by the garbage collector.
Example
public class MyClass
{
public static int a;
~MyClass()
{
}
}
When the C# compiler compiles a destructor, it implicitly translates the destructor code to the equivalent of the Finalize() method. That ensures that the **Finalize() **method of the parent class is executed.
When a C++ object is destroyed, its destructor runs immediately and automatically. However, because of the way the GC works, when using C#, there is no way to know when an object's destructor will actually execute. Another problem with C# destructors is that the implementation of a destructor delays the final removal of an object from memory. An object that does not have a destructor is removed from memory in one pass of the GC but objects that have destructors require two passes to be destroyed. The first pass calls the destructor without removing the object and the second pass actually deletes the object. If you use destructors frequently and use them to execute lengthy clean-up tasks, the impact on performance can be noticeable.
IDisposable
When a class implements it, normally tells you that the class has unmanaged resources that should be released in a deterministic manner.
http://www.c-sharpcorner.com/UploadFile/91c28d/handle-unmanaged-resources/Images/123.png
Notice that it has only one method, Dispose, and it is within this method's implementation that the dirty work is done. Thus, you should completely clean up your object and release all resources inside Dispose. Even though the client code, rather than the system, calls Dispose automatically, it's the client code's way of saying, “I'm done with this object and don't intend to use it ever again.”
IDisposable is defined in the system namespace in the Core .NET assembly mscorlib, in other words, any class in the base class library or the wider framework or any third-party code can implement IDisposable and you need to know if you are using an instance of a class that disposable, so you can use it properly.
Implementation of IDisposable interface
class Resource : IDisposable
{
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// can clean up other managed objects
}
// clean up unmanaged resources
disposed = true;
}
}
~Resource()
{
Dispose(false);
}
}
class Program
{
static void Main()
{
Resource resource = null;
try
{
resource = new Resource();
// use resource
}
finally
{
if (resource != null)
{
resource.Dispose();
}
}
}
}
First notice that Dispose has no parameters and the finalizer call Dispose, has a bool parameter. If called with true, via Dispose without parameters, in other words, this object is being called by user code. When called via the finalizer, with false as the parameter, this object is being cleaned up via the GC.
After making sure that this object is cleaned up only once, checking the disposed variable, the Dispose (bool) method checks to see whether it was called by either user code or the GC. The difference is significant because you don't ever want the GC thread to try cleaning up other managed objects. You see, it is possible that the GC has already cleaned up those objects and you would cause an error, an ObjectDisposedException, by trying to access an object that no longer exists. Regardless of whether this object is being called via user code or the GC, you always need to clean up unmanaged resources, such as OS handles and GDI objects.
Notice that the second statement of Dispose, with no parameters, calls SuppressFinalization. This ensures that the GC won't try to call the finalizer. That wouldn't make sense because we just cleaned up the object. It is also more efficient because it helps avoid a second pass of the GC for finalization. The Program class instantiates an object of type DisposableClass. By performing actions in a try/finally block, the program guarantees that the Dispose method will always be called, thus releasing resources immediately when they are no longer needed.
Using Statement
The following code accomplishes the exact same thing as the try/finally block in the preceding code.
using (Resource r = new Resource())
{
// use resource
}
After executing the using statement, the Dispose() method will be called automatically. The parameter to the using statement must be an IDisposable object, otherwise, a compile-time error will occur.
Example: Using statement without IDisposable object
http://www.c-sharpcorner.com/UploadFile/91c28d/handle-unmanaged-resources/Images/456.png
**Conclusion **
In this article the basics of Finalizer, IDisposable() and using statements are covered, These are the key features in .NET to handle unmanaged resources.