The IComponent Interface

The IComponent Interface

To make matters a little confusing, the .NET Framework also defines a component as a class that implements the System.ComponentModel.IComponent interface or that derives from another class that implements IComponent (such as System.ComponentModel.Component). These IComponent classes have a few characteristics that distinguish them from ordinary classes:

  • They provide a way to release resources deterministically. Because the IComponent interface extends the System.IDisposable interface, every component provides a Dispose method. When this method is invoked, the component immediately frees the resources it uses assuming you've added the necessary code.

  • They provide basic design-time support that is, the ability to be hosted in the Microsoft Visual Studio .NET Toolbox, dragged and dropped on a form, and configured through the Properties window. This basic support is a free frill built into Visual Studio and the .NET Framework.

In this book, I use a slightly broader definition of components that includes any class or group of classes intended for reuse in different applications or for execution on different computers. This means, for example, that Web service classes are components. In strict .NET terms, Web services usually implement IComponent indirectly but they don't need to.

If you'd like, you can change the CustomerDB and CustomerDetails classes into IComponent-supporting types. First of all, you can derive CustomerDB from the System.ComponentModel.Component class. Because this class derives from System.MarshalByRefObject, you won't only gain design-time support you'll also gain the ability to execute the CustomerDB component on another computer and communicate with it through .NET Remoting. The following code shows the new CustomerDB class declaration:

 Public Class CustomerDB   Inherits System.ComponentModel.Component 

CustomerDetails, on the other hand, can derive from System.ComponentModel.MarshalByValueComponent, which is a component type that can't be manipulated remotely. However, you can add the <Serializable> attribute and gain the ability to send objects based on this class from computer to computer using .NET Remoting:

 <Serializable()> _ Public Class CustomerDetails   Inherits System.ComponentModel.MarshalByValueComponent 

We'll examine the <Serializable> attribute and the MarshalByRefObject class in more detail in Chapter 4 when we dive into .NET Remoting. For now, it's just important to understand that you can build on the basic CustomerDB and CustomerDetails classes by making them remotable (or turning them into Web services), and by adding design-time support through the IComponent interface.

Using Components at Design Time

You can access a component from another application in several ways after you compile it. The first way is to add a reference. (In Visual Studio .NET, you right-click on the project in Solution Explorer, choose Add Reference, and find the DLL assembly that contains your component.) You can then use the component classes as if they are a part of your own project:

 Dim CustDB As New DataComponent.CustomerDB CustDB.AddUser(New DataComponent.CustomerDetails( _     "Matthew", "MacDonald", "opensesame")) 

If these classes implement the IComponent interface, however, you have the choice of a second, similar approach that makes use of the design-time capabilities of components. In Visual Studio .NET, right-click on the Toolbox and choose Customize Toolbox. Then add the .NET assembly. Two new icons will appear in the Toolbox, one for each component (as shown in Figure 2-1). At the same time, a reference is added to your project.

Figure 2-1. Components in the Visual Studio .NET Toolbox

graphics/f02dp01.jpg

You can now create the component at design time by dragging it onto a Windows Form, a Web page, or a Web service. The first time you add the component to any part of a project, Visual Studio .NET adds the necessary assembly reference. The component then appears in a special component tray (as shown in Figure 2-2) and is added to the hidden designer code for the class you're creating. You can modify properties for this instance of the component through the Properties window. Typically, this ability to use the Properties window at design time isn't much more than a programming convenience.

Figure 2-2. A component at design time

graphics/f02dp02.jpg

The final way to load a component from an assembly is through reflection (which is similar to late binding). This approach is slower, much more susceptible to runtime errors (for example, if you specify incorrect type information), and more difficult to program with. It's required in some situations where you might be examining a type that you don't know much about, but it's never used as part of a distributed system that you create. Reflection does serve a purpose when creating highly configurable applications, and we'll consider it in more detail in Chapter 15.

Note

Adding an assembly reference to a Visual Basic .NET project is quite different from referencing a COM component in a Visual Basic 6 project. In the .NET world, adding the reference actually copies the assembly DLL to your local application directory. This is because, by default, all components are private and can be located and used only if they are in the same directory as the application.


Resources and Disposable Classes

The CLR uses garbage collection to free up memory by deallocating objects that are no longer referenced in your application. This automatic memory management solves the problems traditionally faced by C++ developers, who needed to ruthlessly track and release every morsel of memory that came under their control. However, it also introduces the problem of nondeterministic finalization. Essentially, nondeterministic finalization means that when you release an object, it might remain in memory until system memory becomes scarce and the garbage collection service springs into action. This works well for ordinary classes but not so well for classes that hold unmanaged resources (such as file handles). In this case, these scarce resources are held onto long after they are needed, potentially reducing application performance or causing a contention error as more than one object competes for the same resource.

Components deal with the problem of nondeterministic finalization through the IDisposable interface, from which IComponent derives. To satisfy IDisposable, every class must provide a Dispose method that automatically releases resources. The drawback is that you need to rely on the client to call this method to clean up when a task is complete. Otherwise, the same performance and contention problems will remain. Many .NET classes, such as FileStream (which wraps a file handle), provide a Dispose method, which you should call when a task is complete, just before setting the object reference to Nothing or letting it go out of scope. In addition, these classes often add custom methods such as Close, which will implicitly call Dispose.

Using the Disposable Pattern in a Component-Derived Class

When you're creating classes that hold limited resources, you need to ensure that they follow the disposable pattern and release their resources explicitly. Ideally you won't need to maintain any resources in a class because this can seriously affect scalability. For example, the CustomerDB class creates, opens, and closes a database connection in each method to conserve that limited resource. This is the best design for a distributed application. Still, you might need to create a lower-level class that wraps a limited resource, which would then be used by a higher-level service provider class. (We'll consider one example of this design in Chapter 9.) If your resource class derives from a .NET Component class, you use the design pattern shown in Listing 2-2.

Listing 2-2 The disposable pattern in a component class
 Public Class MyComponent     Inherits System.ComponentModel.Component     Protected Overloads Overrides Sub Dispose(disposing As Boolean)          If disposing Then              ' Disposal was triggered manually by the client.             ' Call Dispose() on any contained objects.         End If         ' Release unmanaged resources.         ' Call Dispose on the base class.         MyBase.Dispose(disposing)    End Sub End Class 

The client releasing your component won't call your Dispose method directly. Instead, the client calls a basic parameterless Component.Dispose method, which your class inherits. This method automatically calls your custom Dispose method. This allows a component to dispose of itself in two ways: in response to a Dispose method invocation (the preferred way) and when the object is garbage collected (if the client forgot to call Dispose). In the first case, the disposing parameter is True. In the latter case, it is False.

The reason for this extra layer of indirection is that it isn't safe to dispose an object that no longer exists. If the Dispose method has been called by the runtime, there is a good possibility that any contained objects have already been garbage collected, so your object shouldn't attempt to dispose of them again. If Dispose is called by the client, these objects will still exist and should be disposed of properly.

Using the Disposable Pattern in Non-Component Classes

In some cases, you won't be able to derive from System.ComponentModel.Component. For example, you might need to inherit functionality from another non-Component class. Because .NET doesn't support multiple inheritance, you must implement the IComponent or IDisposable interface on your own. The code you use to release resources is similar, but you must add a little more of the lower-level logic yourself. Namely, you need to add a Dispose method with no arguments, which the client calls directly. This method calls the custom Dispose method to release resources and then instructs .NET that garbage collection isn't needed because the object has already cleaned up after itself.

You also need to account for the possibility that the client will forget to use the Dispose method by adding a Finalize method. This method also triggers the custom Dispose method. Listing 2-3 shows a full example.

Listing 2-3 Implementing IDispose directly
 Public Class MyComponent     Inherits MyBaseClass     Implements System.ComponentModel.IComponent     ' Implement IDisposable.     ' This is the method the client calls to dispose the object.     Public Overloads Sub Dispose()        Dispose(True)        ' Take the object out of the finalization queue so it won't be         ' accidentally garbage collected twice.        GC.SuppressFinalize(Me)     End Sub     ' This method is only called if the object is garbage     ' collected without being properly disposed.     Protected Overrides Sub Finalize()         Dispose(False)     End Sub     ' The custom code for releasing resources goes here.     Protected Overloads Overridable Sub Dispose(disposing As Boolean)         If disposing Then             ' Disposal was triggered manually by the client.             ' Call Dispose() on any contained objects.         End If              ' Release unmanaged resources.     End Sub End Class 

Remember, if you don't need to use the Disposable pattern and add design-time support to your component, you don't need to worry about inheriting from the Component class or implementing the IComponent interface. However, it's important to understand how .NET defines components.

Note

If you're a seasoned enterprise programmer, you already know that you can't rely on clients to behave properly and follow details such as calling a Dispose method. That's one of the reasons that service provider classes are always stateless, thereby ensuring that these problems can't occur. However, you might need to develop a low-level disposable object to support your service provider class. (This is analogous to the way the stateless CustomerDB class uses the disposable SqlConnection class.) In this case, the client never directly interacts with the object and you can enforce proper disposal in your server-side code.




Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net