Monday, January 25, 2010

Finalize and Dispose

References:
Rules for Finalize and Dispose

Finalize and Dispose are all about freeing unmanaged resources such as files, streams, db connections etc. held by objects. While Dispose can be directly called by users of objects, Finalize is called by GC.

Do not assume Dispose will be called by the users/programmers using your object. You should always free up resources in Finalize as a back-up.

Calls must be propagated correctly through containmnet hierarchy i.e.
1. If object A has a reference to object B which in turn has reference to object C, then you must make sure that A.Dispose() code calls B.Dispose() (and B.Dispose() calls C.Dispose()).
2. A.Dispose() should also call A's base type's Dispose() if there is a base type implementing IDisposable.

When a class implements IDisposable, it is basically announcing it is using scarce resources. SAM: So your own object (whether stand-alone, abstract base or derived) can and should implement IDisposable, if it's holding references to unmanaged resources on its own or it contains objects that hold references to unmanaged resources. There is no reason why you can't implement your own IDisposable on an object that derives from another object that already implements that interface. However, depending on stand-alone, abstract or derived class you are coding, you may or may not have to implement your own IDisposable. Bottom line is, calling Dispose() by users should free up all the resources, containment-wise and object hierarchy-wise.

Close() should be a special case of Dispose(). Some unmanaged resources like streams have a state of either "open" or "closed", so it makes sense to provide a Close() method to the users of your class instead of Dispose(). However, calling Close() should internally call Dispose().




Example
==========

[C#]
using System;
using System.ComponentModel;

// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.

public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
// Pointer to an external unmanaged resource.
private IntPtr handle;
// Other managed resource this class uses.
private Component component = new Component();
// Track whether Dispose has been called.
private bool disposed = false;

// The class constructor.
public MyResource(IntPtr handle)
{
this.handle = handle;
}

// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
// SAM: Comment on "do not make this virtual". True only if the class you are
// writing is the base class like this example. However, a lot of times, you
// would be writing a class, derived from another class (your own or supplied
// by a software vendor) that DOES make this method virtual and rightfully
// so. Look at ActiveReports software example below.

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.
GC.SuppressFinalize(this);
}

// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
component.Dispose();
}

// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
}
disposed = true;
}

// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);

// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
}
}






Active Reports Example
========================
Background info: From a client's standpoint, you call a method on a server
class "ProfileSummaryActiveReport" (which you also create but ultimately derive from
vendor's ActiveReports3 class) to render a report with "using". ActiveReport3 class
provides "virtual Dispose (bool disposing)" method. See how I have implemented my
client and server code to free up unmanaged resources and references to objects with
unmanaged resources. I could have implemented my own IDisposable on server class,
but I did not because I did not have to (since ActiveReport3 provided me the virtual
method to override).

Further, I architected "ProfileSummaryActiveReport" class to be simply a collection
of Activereports-provided SubReports. Each subreport is a ActiveReports-provided
tabular textual controls or it could be another vendor called ChartFX' chart control.

Idea is, when the client calls the method on ProfileSummaryActiveReport object
with "using", the Dispose() that fires should then propagate thru all objects in a
meaningful way i.e. call dispose() on each subreports.

//----------------------------------------------------------------------
Client calling

ProfileSummaryActiveReport lProfileSummaryActiveReport = null;
RenderReportResult lRenderReportResult = null;
using (lProfileSummaryActiveReport = new ProfileSummaryActiveReport(
aLogger,
lActiveReportCount,
lPageDataSet,
aReportParams,
pdfParams,
aProfileSummaryDiagnosticsInfo))
{
lRenderReportResult = lProfileSummaryActiveReport.RenderReport();
}

//----------------------------------------------------------------------
Servers

//Write your own server-based active report (must derive from vendor
//supplied class ActiveReport3) that serves as a container for subreports

public partial class ProfileSummaryActiveReport : ActiveReportBase
{
public List ListOfSubReportWrapper = new List();

public List ListOfPlaceholderSubReportWrapper = new List();

private void DisposeSubReports()
{
foreach (SubReportWrapper lSRW in this.ListOfSubReportWrapper)
{
lSRW.ARSubReport.Dispose();
}

foreach (SubReportWrapper lSRW in this.ListOfPlaceholderSubReportWrapper)
{
lSRW.ARSubReport.Dispose();
}
if (this.ReportPageFootnotes != null)
this.ReportPageFootnotes.Clear();

if (this.SubReportForcedHeightInfoList != null)
this.SubReportForcedHeightInfoList.Clear();

foreach (KeyValuePair lKVP in this.mDictionaryProfileSummarySubReport)
{
if (lKVP.Value.SubReportDataSet!=null)
lKVP.Value.SubReportDataSet.Dispose();
}
}


//blah blah
}

partial class ProfileSummaryActiveReport
{
///
/// Clean up any resources being used.
///

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (this.mReadyToDisposeSubReportsIfAny)
{
this.DisposeSubReports();
}
}
base.Dispose(disposing);
}

private DataDynamics.ActiveReports.Detail detail;
//blah blah
}

//----------------------------------------------------------------------

public partial class ActiveReportBase : DataDynamics.ActiveReports.ActiveReport3
{
protected System.Data.DataSet mReportData = null;

public byte[] CombineReportsAsPDFByteArray(Dictionary aDictionaryOfReports)
{
DataDynamics.ActiveReports.ActiveReport3 lActiveReport = new ActiveReport3();
int lPageNumber = 0;
foreach (KeyValuePair kvp in aDictionaryOfReports)
{
lPageNumber++;
using (MemoryStream lMemoryStream = new MemoryStream(kvp.Value))
{
this.Document.Load(lMemoryStream);
foreach (Page lPage in this.Document.Pages)
{
lActiveReport.Document.Pages.Add(lPage);
}
}
}

using (MemoryStream lMemoryStream = new MemoryStream())
{
using (PdfExport lPdfExport = new PdfExport())
{
lPdfExport.Export(lActiveReport.Document, lMemoryStream);
}
lMemoryStream.Flush();
lActiveReport.Dispose();
this.mReadyToDisposeSubReportsIfAny = true;
return lMemoryStream.ToArray();
}
}

//blah blah
}

partial class ActiveReportBase
{

///
/// Clean up any resources being used.
///

protected override void Dispose(bool disposing)
{
if (disposing)
{

}
base.Dispose(disposing);
}

private DataDynamics.ActiveReports.Detail detail;
//blah blah

}

//----------------------------------------------------------------------

public partial class GanttChartFX : ProfileSummarySubReportARBase, IBarBasedChart
{
private ChartFX.WinForms.Chart mChart = null;
private DataDynamics.ActiveReports.CustomControl mCustomControl = null;

//blah
}

partial class GanttChartFX
{

///
/// Clean up any resources being used.
///

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (this.mChart != null)
this.mChart.Dispose();

if (this.mCustomControl != null)
this.mCustomControl.Dispose();
}
base.Dispose(disposing);
}

private DataDynamics.ActiveReports.Detail detail;
// blah
}

//----------------------------------------------------------------------

public abstract partial class ProfileSummarySubReportARBase : DataDynamics.ActiveReports.ActiveReport3, IProfileSummarySubReportRenderer, IPlaceholderSubReportHost
{
// blah
}

partial class ProfileSummarySubReportARBase
{

///
/// Clean up any resources being used.
///

protected override void Dispose(bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}

private DataDynamics.ActiveReports.Detail detail;
// blah
}

No comments: