In this post, 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 post. 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 then called the Dispose
method for that particular DataSet
and this time, the 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 the code snippet below:
FeatureSchema fs = Proxy.GetFeatureSchema();
fs.Dispose();
foreach (FeatureSchema.FeatureRow featureRow in fs.Feature.Rows)
{
}
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
{
~FeatureSchema ()
{
}
}
As displayed below, I am loading data to this typed dataset from a .xml file:
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”.
This verifies the need of calling GC.SuppressFinalize
from the constructor of DataSet
/DataTable
to prevent the garbage collector from requesting finalization.
Even though Dispose
in DataSet
/DataTable
does nothing, we should not ignore it and stick to the .NET coding practices, otherwise in future versions of Framework, things may break. Please do correct me if I am wrong. This concludes the reason behind this post.
I’ll continue with the main theme of the post. The guidelines for implementers are:
-
IDisposable
If a class uses external resources and will not be used on a design surface IDisposable
interface has to be implemented directly or indirectly e.g. System.Drawing.Font
class, etc.
-
IComponent
If a class will be used on a design surface IComponent
interface has be implemented directly or indirectly. IComponent
implements IDisposable
e.g. System.Web.UI.Control
class, etc.
-
Component
If a class will be used on a design surface and is Marshalled by reference, then it has to derive from Component
class e.g. System.Timers.Timer
class, etc.
-
MarshalByValueComponent
If a class will be used on a design surface and is Marshalled by value, then it has to derive from MarshalByValueComponent
class, e.g. DataSet
, DataTable
, etc.
-
Control
If a class will be used on a design surface and provides a user interface, then this class is a control and has to derive from System.Windows.Form.Control
or System.Web.UI.Control
, e.g. System.Windows.Forms.Button
, etc.
The above discussion does not apply to WPF. The WPF Designer architecture is significantly different from the Windows Forms Designer architecture, which is characterized by the IComponent
interface and the System.ComponentModel
namespace. The WPF Designer architecture retains the TypeConverter
and TypeDescriptor
classes from the Windows Forms Designer object model. Most other aspects of the WPF Designer architecture are different. For more information, please read Comparing the Windows Forms Designer Framework to the WPF Designer Framework.
Reference
del.icio.us Tags:
.NET,
.NET Framework,
IDisposable,
IComponent,
Component,
MarshalByValueComponent,
Control,
Windows Forms,
ASP.NET,
DataSet,
DataTable,
Garbage Collector,
GC