In this article I’ll discuss about the criteria behind choosing IDisposable, IComponent, Component, MarshalByValueComponent and base Control classes(System.Windows.Form.Control and System.Web.UI.Control) while implementing a Class.
Prior to discussing further I’d like to share the reason behind this article. I ran code analysis in a project code and got a warning that Dispose was not called for a DataSet in the project’s code. I than called the Dispose method for that particular DataSet and this time code analysis executed with zero errors/warnings. Out of curiosity I thought of verifying the use of calling Dispose on DataSet as even though when Dispose was not being called everything was fine. I found that calling Dispose method did nothing as DataSet is a managed object and does not contain unmanaged resources(if I’m wrong please correct me on this). I could verify this as I was able to access/update the DataSet even after Dispose had been called without any “Object Disposed Exception”. FeatureSchema is a typed DataSet as displayed in code snippet below
FeatureSchema fs = Proxy.GetFeatureSchema();
fs.Dispose();
foreach (FeatureSchema.FeatureRow featureRow in fs.Feature.Rows)
{
//Do procesing on dataset
}
I went one step further to find the reason behind having Dispose method in DataSet/DataTable and the reason was inheritance as DataSet/DataTable inherits from System.ComponentModel.MarshalByValueComponent class. The reason DataSet/DataTable inherits MarshalByValueComponent class so that it can be designable i.e. used on a design surface. Thus DataSet/DataTable has dispose method even though it doesn’t clean up the resources. DataSet/DataTable call GC.SuppressFinalize in the constructor to prevent the Garbage Collector from calling Object.Finalize on an object that does not require it.
This can be proved by the code snippet displayed below
I have a typed dataset FeatureSchema and I have added a destructor to it. I have excluded the code that was auto generated as I am focusing on finalizers only
public partial class FeatureSchema : global::System.Data.DataSet
{
//Added only a destructor to this types dataset. I have excluded all other code
// as I am focusing on finalizers
~FeatureSchema ()
{
}
}
As displayed below I am loading data to this typed dataset from an .xml file
//I am just loading the data into this typed dataset
FeatureSchema schema = new FeatureSchema();
schema.ReadXml(featureSchemaFile, XmlReadMode.Auto);
As DataSet calls GC.SuppressFinalize in the constructor Object.Finalize will not be called by the Garbage Collector. This can be verified by placing a break point on the destructor and you will find that it is not getting called.
Now I will call GC.ReRegisterForFinalize as displayed in the code snippet below
FeatureSchema schema = new FeatureSchema();
GC.ReRegisterForFinalize(schema);
schema.ReadXml(featureSchemaFile, XmlReadMode.Auto);
Calling GC.ReRegisterForFinalize will register the object for finalization. I am doing this as I know that DataSet calls GC.SuppressFinalize in the constructor. Now the code will break on the break point which I placed on the constructor at some random time i.e. when GC will execute the “GC Finalizer Thread”.
Read more: Application design and programming in .NET
Prior to discussing further I’d like to share the reason behind this article. I ran code analysis in a project code and got a warning that Dispose was not called for a DataSet in the project’s code. I than called the Dispose method for that particular DataSet and this time code analysis executed with zero errors/warnings. Out of curiosity I thought of verifying the use of calling Dispose on DataSet as even though when Dispose was not being called everything was fine. I found that calling Dispose method did nothing as DataSet is a managed object and does not contain unmanaged resources(if I’m wrong please correct me on this). I could verify this as I was able to access/update the DataSet even after Dispose had been called without any “Object Disposed Exception”. FeatureSchema is a typed DataSet as displayed in code snippet below
FeatureSchema fs = Proxy.GetFeatureSchema();
fs.Dispose();
foreach (FeatureSchema.FeatureRow featureRow in fs.Feature.Rows)
{
//Do procesing on dataset
}
I went one step further to find the reason behind having Dispose method in DataSet/DataTable and the reason was inheritance as DataSet/DataTable inherits from System.ComponentModel.MarshalByValueComponent class. The reason DataSet/DataTable inherits MarshalByValueComponent class so that it can be designable i.e. used on a design surface. Thus DataSet/DataTable has dispose method even though it doesn’t clean up the resources. DataSet/DataTable call GC.SuppressFinalize in the constructor to prevent the Garbage Collector from calling Object.Finalize on an object that does not require it.
This can be proved by the code snippet displayed below
I have a typed dataset FeatureSchema and I have added a destructor to it. I have excluded the code that was auto generated as I am focusing on finalizers only
public partial class FeatureSchema : global::System.Data.DataSet
{
//Added only a destructor to this types dataset. I have excluded all other code
// as I am focusing on finalizers
~FeatureSchema ()
{
}
}
As displayed below I am loading data to this typed dataset from an .xml file
//I am just loading the data into this typed dataset
FeatureSchema schema = new FeatureSchema();
schema.ReadXml(featureSchemaFile, XmlReadMode.Auto);
As DataSet calls GC.SuppressFinalize in the constructor Object.Finalize will not be called by the Garbage Collector. This can be verified by placing a break point on the destructor and you will find that it is not getting called.
Now I will call GC.ReRegisterForFinalize as displayed in the code snippet below
FeatureSchema schema = new FeatureSchema();
GC.ReRegisterForFinalize(schema);
schema.ReadXml(featureSchemaFile, XmlReadMode.Auto);
Calling GC.ReRegisterForFinalize will register the object for finalization. I am doing this as I know that DataSet calls GC.SuppressFinalize in the constructor. Now the code will break on the break point which I placed on the constructor at some random time i.e. when GC will execute the “GC Finalizer Thread”.
Read more: Application design and programming in .NET