In the .NET Framework, a component is a class that implements the IComponent interface or that derives directly or indirectly from a class that implements IComponent. In programming, the term component is generally used for an object that is reusable and can interact with other objects. A .NET Framework component satisfies those general requirements and additionally provides features such as control over external resources and design-time support.
Control over external resources
The IComponent interface extends the IDisposable interface, which has a method named Dispose()()() in its contract. In its implementation of the Dispose()()() method, a component must release external resources explicitly. This provides a deterministic way to free resources, in contrast to the default nondeterministic cleanup that happens through garbage collection. Developers must propagate Dispose()()()throughout a containment hierarchy to ensure that children of a component also free resources. Additionally, a derived component must invoke the Dispose()()() method of its base class.
Note: |
|---|
Even when you provide explicit control over resources through
Dispose()()(), you should always provide implicit cleanup through the finalizer (destructor) to prevent resources from permanently leaking if a user fails to call Dispose()()() on your component.
|
The following example shows the pattern for implementing Dispose()()() in a base component and in a derived component.
public class BaseComponent : IComponent {
// IComponent extends IDisposable.
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
}
// Simply call Dispose(false).
~BaseComponent(){
Dispose (false);
}
}
// Derived component.
public class DerivedComponent : BaseComponent {
protected override void Dispose(bool disposing) {
if (disposing) {
// Free other state.
}
// You must invoke the Dispose method of the base class.
base.Dispose(disposing);
// Free your own state.
...
}
// No finalizer/destructor.
// No Dispose() method.
}
' Design pattern for a base class.
Public Class BaseComponent
Implements IComponent
' Implement IDisposable
Public Overloads Sub Dispose()
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposing Then
' Free other state (managed objects).
End If
' Free your own state (unmanaged objects).
' Set large fields to null.
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Dispose (False)
End Sub
End Class
' Design pattern for a derived component.
Public Class DerivedComponent
Inherits BaseComponent
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposing Then
' Release managed resources.
End If
' Release unmanaged resources.
' Set large fields to null.
' Call Dispose on your base class.
Mybase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method with parameters because it inherits
' them from the base class.
End Class
Design-Time Support
An important feature of components in the .NET Framework is that they are designable, which means that a class that is a component can be used in a rapid application development (RAD) environment such as Visual Studio. A component can be added to the toolbox of Visual Studio, can be dragged and dropped onto a form, and can be manipulated on a design surface. Note that base design-time support for IComponent types is built into the .NET Framework; a component developer does not have to do any additional work to take advantage of the base design-time functionality.
For more information about design-time support, see Design-Time Attributes for Components and Extending Design-Time Support.
Hosting a Component
A component can be sited (hosted) in a container (defined later in this topic). When a component is sited, it interacts with the container through its site (defined later in this topic) and has the ability to query and get services from its container through the site. To ensure that resources are released when a container is torn down, a container must implement the IDisposable interface. In its implementation of the Dispose()()() method, a container must release all resources that it holds and invoke the Dispose()()() method of each of its contained components.
Containment is logical and need not have a visual representation. A middle-tier container that sites database components is an example of nonvisual containment. Visual containment is seen in the Windows Forms designer and the Web Forms designer in Visual Studio. The visual design surface is a container that hosts the form component (in Web Forms, the page component).
Marshaling a Component
Components can be remotable or nonremotable. Remotable components are marshaled by reference or by value. Marshaling involves sending objects across boundaries such as application domains (lightweight processes), processes, and even machines. When an object is marshaled by reference, a proxy is created that makes remote calls to the object. When an object is marshaled by value, a serialized copy of the object is sent across the relevant boundary.
Remotable components that encapsulate system resources, that are large, or that exist as single instances should be marshaled by reference. The base class for components that are marshaled by reference is Component. This base class implements IComponent and derives from MarshalByRefObject. Many components in the .NET Framework class library derive from Component, including Control (the base class for Windows Forms controls), WebService (the base class for XML Web services created using ASP.NET), and Timer (a class that generates recurring events).
Remotable components that simply hold state should be marshaled by value. The base class for components that are marshaled by value is MarshalByValueComponent. This base class implements IComponent and derives from Object. Only a few components in the .NET Framework class library derive from MarshalByValueComponent. All such components are in the System.Data namespace (DataColumn, DataSet, DataTable, DataView, and DataViewManager).
Note: |
|---|
The base classes for objects that are marshaled by value and by reference are
Object and MarshalByRefObject, respectively, but the corresponding derived classes are named MarshalByValueComponent and Component. The logic behind the naming scheme is that the more commonly used type has the simpler name.
|
If a component will not be remoted, do not derive from the base implementations for Component; instead, implement IComponent directly.
For more information about object remoting, see .NET Remoting Overview.