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.
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.
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
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
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.
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
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.
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
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