Sunday, November 14, 2010

Reasons for using IDisposable: Cleaning Up Unmanaged Recourses.

About C# Disposal pattern,
How to code Disposal pattern for unmanaged resource.
Difference between Managed and Unmanaged resource.
The garbage collector (GC) in .NET is a core part of the .NET Common Language Runtime (CLR) and is available to all .NET programming languages. The GC was never meant to manage resources; it was designed to manage memory allocation, and it does an excellent job at managing memory allocated directly to native .NET objects. It was not designed to deal with unmanaged memory and operating system allocated memory, so it becomes the responsibility of the developer to manage these resources. (From MSDN)
Fortunately, the .NET Framework provides many abstractions to hide the complexity of dealing with unmanaged resources. In fact, many of the CLR classes make use of these unmanaged resources on your behalf and handle all of the resource management transparently. It is programmer’s responsibility to release this unmanaged resource, if he wishes to release it before finalize is called by GC, Explicitly by using Dispose().
Unmanaged Resources like :
1. Usage of stream reader to read a file
2. Usage of stream writer to write contents into a file
3. Establishing database connectivity,even connection object is managed resource it holds unmanaged resources like socket etc to connect to remote server. etc..

The CLR does provide a way to implement deterministic finalization through the IDisposable interface, a Dispose method for explicit clean up, and in some cases, a Finalize method for implicit clean up
IDisposable :
The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur. Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files, streams and unmanaged resources hold by your managed resources(objects).
Dispose() :
Dispose method performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times. Instance methods other than Dispose can throw an ObjectDisposedException when resources are already disposed.
Because the Dispose method must be called explicitly, objects that implement IDisposable must also implement a finalizer to handle freeing resources when Dispose is not called. By default, the garbage collector automatically calls an object's finalizer prior to reclaiming its memory. However, once the Dispose method has been called, it is typically unnecessary for the garbage collector to call the disposed object's finalizer. To prevent automatic finalization, Dispose implementations can call the GC.SuppressFinalize method.
MSDN reference for implementing IDisposable correctly.
More on implementing Finalize and Dispose method for cleaning unmanaged resources.
Following code snippet will show you how to implement IDisposable Interface. I created a console application and added 2 classes viz ‘AnotherResource’ and ‘Resource’ And a class with main method ‘Program’ in dummy namespace ‘disposableStuff’ for my application.
Now instead of writing huge explanation for each line of code I’ve written comment in code. Please go through code, do not miss comments though. Code is taken from MSDN examples and modified a bit.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
//AnotherResource may or may not be having unmanaged resource in this case not
public class AnotherResource : IDisposable
{
String s;
public AnotherResource()
{
s = "hecxvcxc";
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
s = null;
}
}
}

public class Resource : IDisposable
{
//unmanaged resource
private IntPtr nativeResource = System.Runtime.InteropServices.Marshal.AllocHGlobal(100);

private AnotherResource managedResource = new AnotherResource();

public void Dispose()
{
Dispose(true);

// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue 
// and prevent finalization code for this object
// from executing a second time.
//Reason is that if finalizer is called agin for this then it will throw exception
GC.SuppressFinalize(this);
}
// NOTE: Leave out the finalizer altogether if this class doesn't 
// own unmanaged resources itself, but leave the other methods
// exactly as they are. 
//here dstructor should be used coz , finalizer is destructor, finalizing means destructor is called.
~Resource()
{
// Finalizer calls Dispose(false)
Dispose(false);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
if (managedResource != null)
{
managedResource.Dispose();
managedResource = null;
}
}
// free native resources if there are any.
if (nativeResource != IntPtr.Zero)
{
Marshal.FreeHGlobal(nativeResource);
nativeResource = IntPtr.Zero;
}
}
}





Program.cs
namespace disposableStuff
{
class Program
{
public static void Main()
{
Idea1();///dispose in finally
Idea2();//use of using block //recommended
Idea3();//this is why dispose is useful 
}

/// <summary>
/// This is simple senario were u will dispose rs 
/// once finalizer is called by GC, implicit way of cleaning stuff
/// </summary>
private static void Idea1()
{
Resource rs = new Resource();
try
{
//use rs here..
}
catch (Exception ex)
{
}
finally
{
if (rs != null)
rs.Dispose();
}
}

private static void Idea2()
{
using (Resource rs = new Resource())
{
//use rs here
//dispose will be automatically called
//The using statement ensures that Dispose is called even if an exception occurs 
//while you are calling methods on the object. You can achieve the same result by 
//putting the object inside a try block and then calling Dispose in a finally block; 
//in fact, this is how the using statement is translated by the compiler
}
}

/// <summary>
/// In case if u need 'rs' to be used in here,
/// and need to do other things also. 
/// u will create rs and use it, now rs will hold managed and unmanaged resources which u will
/// not use later once ur purpose is fulfilled, but there is lot of things to be done after that
/// now while this things are done rs will remain in memory unnecessarily. So u will dispose rs
/// and continue with other stuff. 
/// </summary>
private static void Idea3()
{
Resource rs = new Resource();
try
{
//100 lines of code
//use rs here..
rs.Dispose();
//more 10000 lines of code
}
catch (Exception ex)
{
}
}
}
}



0 comments:

Post a Comment