3.4. Controlling How Classes Are Implemented

Although you can generally create custom versions of any of classes that you write or find in the .NET Class Libraries, there will be times when you'll want to control the outcome, especially when others will use your work. There are also times when you'll want to prove to .NET that your class has fulfilled the terms of a contract that promises a certain level of functionality and is therefore qualified to handle particular assignments.

3.4.1. Allowing or Preventing Overridable Methods

In the earlier part of this chapter, you saw how to use the Stack class in the System.Collections namespace and how you can extend its functionality by inheriting from it. You were also able to override and overload some of its methods to suit your own requirements.

In this section, you will learn how you can create classes from which others can inherit. You will also learn how to specially allow or prevent subclasses from changing your methods.

Using the StackClass defined in the last section, suppose you want others, including yourself, to be able to reuse the class and override its methods. In this case, you would do the following:

 Public Class MyStackClass Inherits StackClass End Class  

To override the Push and Pop methods in the base class, you would use the Overrides keyword, as shown in Example 3-12.

Example 3-12. Overriding the Push and Pop methods of MyStackClass
 Public Class MyStackClass  Inherits StackClass Public Overrides Sub Push(ByVal item As Object) … MyBase.Push(item) End Sub Public Overrides Function Pop() As Object … Return MyBase.Pop() End Function End Class 

However, for the Push and Pop methods to be overridden in the base class, you need to give clients permission to do so by adding the Overridable keyword, as shown in Example 3-13.

Without the Overridable keyword, you would not be able to override the methods in the base class.

Example 3-13. Making the Push and Pop methods of StackClass overridable
 Public Class StackClass … Public Overridable Sub Push(ByVal item As Object) If pointer > UBound(element) Then Throw New Exception("Stack is full.") End If element(pointer) = item pointer += 1 End Sub Public Overridable Function Pop() As Object pointer -= 1 If pointer < 0 Then Throw New Exception("Stack is empty.") End If Return element(pointer) End Function End Class 

In this case, the implementations of the methods are provided, and classes that inherit from them can choose to override this implementation if they want to.

3.4.2. Specifying Implementation Details with Abstract Classes and Methods

Suppose one day your designer receives a call from a customer who wants the designer to create a custom car for him. He may tell the designer what his requirements are and what features that he wants for that car, but he does not tell the designer what to do, since it is the designer's job to figure out implementation. In this case, what the customer provides is essentially an abstract class (the request to design a car) along with a list of various abstract methods (the features of the car).

There are times when you will want to define the structure of a class and leave it to a subclass to provide its implementation. For example, you might want to define the methods available in a Stack class but leave the implementation to the subclass (so that implementers can use whatever data structures they prefer to implement the Stack, such as an array). Here's an example of how to do it:

 Public MustInherit Class AbstractStackClass Public MustOverride Sub Push(ByVal item As Object) Public MustOverride Function Pop() As Object End Class 

The MustInherit keyword specifies that the AbstractStackClass class cannot be directly instantiated. The class can be used only if inherited by a subclass. Thus, the following is not valid:

 Dim s1 As New AbstractStackClass '<--not allowed 

The purpose of this class is to provide the base properties and methods for subclasses.

The Push and Pop methods are known as abstract methods. You do not implement an abstract method when you declare it; its implementation is left to the classes that inherit from it. It is logical for this method to be abstract, because the way you push or pop an item into a Stack is dependent on how you implement a Stack internally. You declare an abstract method in VB 2005 with the MustOverride keyword.

To implement the class and its methods, you then inherit from the AbstractStackClass, and then provide the implementation of the methods using the Overrides keyword, as shown in Example 3-14. You can also add additional methods and constructors to the class.

Example 3-14. Implementing the AbstractStackClass abstract class
 Public Class MyStackClass Inherits AbstractStackClass Private element() As Object Private pointer As Integer Public Sub New() ReDim element(100) pointer = 0 End Sub Public Sub New(ByVal size As Integer) ReDim element(size - 1) pointer = 0 End Sub Public Overrides Sub Push(ByVal item As Object) If pointer > UBound(element) Then Throw New Exception("Stack is full.") End If element(pointer) = item pointer += 1 End Sub Public Overrides Function Pop() As Object pointer -= 1 If pointer < 0 Then Throw New Exception("Stack is empty.") End If Return element(pointer) End Function End Class 

Note that in an abstract class, you can still provide implementations for some methods so that subclasses can use them; not all methods must be abstract.

3.4.3. Creating Contracts with Implementers Using Interfaces

An interface is similar to the abstract class, with one notable difference: an interface contains no implementation at all, while an abstract class may specify one or more method implementations.

Consider the interface example in Example 3-15.

Example 3-15. Defining an interface for StackClass
 Interface IStack Sub Push(ByVal item As Object) Function Pop() As Object End Interface 

To implement the interface and the methods contained within it, use the Implements keyword, as shown in Example 3-16.

Abstract Classes Versus Interfaces

The advantage of using an interface is that a class can implement multiple interfaces, but can never inherit from more than a single class at the same time. However, when you implement an interface (or interfaces), you need to implement all of the methods specified by the interface, since the interface itself cannot have any implementation. An abstract class, on the other hand, can define the implementation for some of its methods, but you can only inherit from one abstract class.

Example 3-16. Implementing the IStack interface
 Public Class MyStackClass Implements IStack Public Function Pop() As Object Implements IStack.Pop … End Function Public Sub Push(ByVal item As Object) Implements IStack.Push … End Sub End Class 

VB 2005 allows you to implement multiple interfaces.

3.4.4. Controlling the Destruction of Objects

To dereference an object i.e., remove the reference to an object that you have created you can simply set the object variable to Nothing, like this:

 Dim ms1 As New MyStack() ms1 = Nothing 

To dereference an object, you need not necessarily set it to Nothing. When an object variable goes out of scope (such as when reaching the end of a function), the variable will be dereferenced automatically.

Once an object is dereferenced, the runtime will perform a garbage collection when the memory pressure gets high enough (i.e., the system begins to run out of memory) to reclaim the memory used by the object. The garbage collector will call the Finalize method. You cannot call it directly.

It is not guaranteed that the Finalize method will be called immediately when an object is dereferenced. This timing of this is entirely up to the garbage collector in the CLR.

The Finalize method is a good place for you to place code that frees up resources, especially if your object uses unmanaged objects (such as database handles or COM objects, and so on; the resources used by these objects would not be freed up automatically):

 Protected Overrides Sub Finalize() ' code to release objects explicitly End Sub 

VB 6 Tip: The Sub Finalize procedure in VB 2005 replaces the Class_Terminate method used in VB 6 and earlier versions. However, unlike the Class_Terminate method, the Finalize procedure is not guaranteed to execute immediately after setting an object to Nothing.

Because calling the Finalize method will add overhead to the execution of your application, you should implement Finalize only when necessary. Also, the Finalize method is not guaranteed to ever be called; it depends on the shutdown conditions of the runtime.

Since you can't really determine when an object's resource will be freed up, you can use the second type of method supported in VB 2005, the Dispose method, and place your code for freeing up the resources there. To use the Dispose method, you need to implement the IDisposable interface and implement the IDisposable.Dispose method, as shown in Example 3-17.

Example 3-17. Using the Dispose method
 Public Class MyStack Inherits Stack Implements IDisposable … Protected Overrides Sub Finalize() ' code to release objects explicitly End Sub Public Sub Dispose() Implements _ IDisposable.Dispose ' code to release objects explicitly End Sub … 

The advantage of using the Dispose method is that you can explicitly call it to free up all your resources:


When you do not call the Dispose method explicitly, you should also call it in Finalize. (Note that in Example 3-17, you make a call to the Dispose method in the Finalize method.) Hence, you need to make sure that the code in the Dispose method is safe to be called multiple times.

Disposing of Resources

Often you need to create and use some resources and then immediately release the resources so that memory can be reclaimed. VB 2005 comes with a new construct known as Using…End Using. The Using…End Using construct guarantees that the resources acquired within the Using block will be disposed of after the execution of the block. Consider the following:

 Public Sub Data_Access( _ ByVal str As String) Using conn As New SqlConnection(str) Dim ds As DataSet '---some code to perform data ' access End Using … … End Sub 

The conn and ds objects are valid only within the Using block. The conn object will be disposed after the execution of the Using block (its Dispose method will get called). The Using block is a good way for you to ensure that resources (especially COM objects and unmanaged code, which would not be unloaded automatically by the garbage collector in the CLR) are properly disposed of after they are no longer needed.

3.4.5. Tagging Objects with Attributes

Attributes are descriptive tags that can be used in VB 2005 applications to provide additional information about types (classes), fields, methods, and properties. Attributes can be used by .NET to decide how to handle objects while an application is running.

Using our car example, the cars of ambassadors often display a flag indicating their status as VIPs so that motorists will give way when they approach. The flag in this case can be likened to an attribute.

Using attributes either those provided by the .NET Framework or those you define yourself gives you additional control over the objects in your applications. Attributes in Visual Basic are used in web services. For example, suppose you wish to expose a TRanslate method in an ASP.NET Web Service project. Prefixing the method with the <WebMethod()> attribute will expose the method as a web service using SOAP, as shown in Example 3-18.

Example 3-18. Using the WebMethod attribute
 <WebMethod()> _ Public Function Translate(ByVal str As String) _ As String … End Function 

Another occasion on which you're likely to use attributes is when you need to use a legacy unmanaged DLL to perform some function in a .NET application. To import the relevant function, you'll need to use the <DllImport()> attribute, as shown in Example 3-19.

Example 3-19. Using the DllImport attribute
 <DllImport("KERNEL32.DLL", EntryPoint:="MoveFileW", _ SetLastError:=True, CharSet:=CharSet.Unicode, _ ExactSpelling:=True, _ CallingConvention:=CallingConvention.StdCall)> _ Public Shared Function MoveFile(ByVal src As String, _ ByVal dst As String) _ As Boolean ' Leave function empty - DLLImport attribute forwards ' calls to MoveFile to MoveFileW in KERNEL32.DLL. End Function 

To use the <DllImport()> attribute, you need to import the System.Runtime.InteropServices namespace in your project.

In Visual Studio 2005, you can use attributes to mark a method in a class as obsolete. Marking a method with the <Obsolete( )> intrinsic attribute causes a warning message to be displayed when someone attempts to use it, as shown in Example 3-20.

Example 3-20. Using the Obsolete intrinsic attribute
 Public Class MyStack  … <Obsolete("This method is obsolete. Use Push(obj as Object)")> _ Public Sub PushStr(ByVal obj As String) MyBase.Push(obj) End Sub … 

Suppose now you tried to use the PushStr method of the MyStack class:

 Dim ms2 As New MyStack() ms2.PushStr("Hello") 

Visual Studio 2005 will signal a warning (not an error though) in the Error List window (see Figure 3-14).

Figure 3-14. The Error List window displaying the warning on the obsolete method

A more thorough discussion of attributes is beyond the scope of this book. For additional information, see Programming Visual Basic 2005 by Jesse Liberty (O'Reilly) or Programming .NET Components by Juval Lowy (O'Reilly).

Visual Basic 2005 Jumpstart 2005
Visual Basic 2005 Jumpstart
ISBN: 059610071X
EAN: 2147483647
Year: 2005
Pages: 86
Authors: Wei-Meng Lee

Similar book on Amazon

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