3.2. Reusing and Customizing Classes


The use of classes in OOP allows you to build an application from discrete components, each of which encapsulates the variables and methods needed to carry out specific tasks. But as powerful as classes are for designing and implementing applications, the ability to reuse and customize them is what makes support for OOP such a powerful tool in Visual Basic 2005.

In this section, you'll see how you can create your own classes by modifying those provided by the .NET Framework. The same principles can also be applied to class libraries that you or your teams develop. In addition, you'll learn about the new generic classes in Visual Basic 2005 that are designed from scratch to be flexible in the range of types they accept.

3.2.1. Using Inheritance

One of the fundamental concepts of OOP is inheritance. Inheritance facilitates code reuse and allows you to extend and use the code that you have already written. Simply put, inheritance is the ability to extend the functionality of classes, and is the basis of the several techniques we discuss in this section.

The beauty of inheritance is that you can define all the common logic you need in a single master class typically called the base or parent class and then use inheritance to extend its logic in a derived class or child class and customize it to suit your own needs. In this section, you will learn how you can inherit from the .NET Stack class, and in the next section you'll learn how you can customize it.

First, let's create a new Windows application using Visual Studio 2005. Name the project MyStackApp. Add a new class item to MyStackApp by right-clicking on the project name in Solution Explorer and then selecting Add New Item…. Select Class and name the class Double-click the MyStack.vb file in Solution Explorer to open it for editing. In the MyStack class, use the Inherits keyword to inherit from the Stack class, as shown in the following snippet:

 Public Class MyStack Inherits System.Collections.Stack End Class 

By using the Inherits keyword, you specify that your MyStack class is to inherit all the methods and properties of the Stack class of the .NET Class Library. You can use MyStack in place of the Stack class. To do so, double-click on the default Form1 in Solution Explorer and then double-click on the design pane to generate the Form_Load event of Form1, as shown in Example 3-3. Enter the code as shown in bold.

Example 3-3. Replacing the Stack class with the MyStack class
 Private Sub Form1_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load Dim ms1 As New MyStack ms1.Push("Hello ") ms1.Pop()  End Sub 

3.2.2. Customizing a Method

In the last section, you saw how to create a customized class by deriving MyStack from the .NET Stack class and how to use it just like the Stack class. However, you may wish to tweak some of the methods available in the MyStack class to suit your own purposes. Like the car designer, you might not be pleased with the performance of the braking system of the previous model. Hence, you would want to redesign and fine-tune the braking system for the new model.

Suppose you want to use a Stack to add two numbers. It turns out that the Push and Pop methods of the .NET class are not optimal for this task. Consider the following code snippet:

 s1.Push(5) s1.Push("S") MsgBox(s1.Pop + s1.Pop) 

The second Push method has pushed a String instead of an Integer onto the Stack. When you try to pop the two values (5 and S) from the stack and perform a mathematical operation on them, you will get a runtime error. This vulnerability arises from the fact that the Push method of Stack accepts an Object parameter. But of course, every .NET type is an object, so Push will accept any data type. If you know ahead of time that your stack will be used for arithmetic operations, it would be useful to restrict the parameters Push will accept to numbers only.

Changing the behavior of the inherited Push method can be done by overriding it. You override a method in VB 2005 by defining a new version that suits your purposes and by indicating that you want to use this version instead of the inherited version with the Overrides keyword, as shown in Example 3-4.

The first thing you will do is override the Push method in the Stack class. Recall that the Push method does not check for the type of the data pushed onto the stack. Assuming that you want the MyStack class to deal only with numeric values, you need to override the implementation of the original Push method with the Overrides keyword. Now the base method Push is no longer accessible.

Example 3-4. Overriding the Push method of Stack
 Public Class MyStack Inherits System.Collections.Stack Public Overrides Sub Push(ByVal obj As Object) If Not IsNumeric(obj) Then Throw New Exception("Non-numeric value in Stack") End If MyBase.Push(obj) End Sub End Class 

The new Push method now checks to ensure that the value pushed into the stack is a numeric value; if it is not, the method throws an exception at runtime:

 ms1.Push(5) ms1.Push("S") ' runtime error 

If the value passed to the Push method is a number, the method calls the Push method of the base class, Stack. The MyBase keyword refers to the base class from which the current class is derived, and its inherited members.

3.2.3. Adding Alternate Versions of a Method to a Class

Your class can offer alternate version of the same method to its users. Adding alternate versions of the same method to a class is known as overloading and is yet another useful object-oriented technique available to VB 2005 programmers.

Let's return to our stack example. In the previous section, you overrode the implementation of the original Push method so it will accept only numeric values. One drawback of this technique, however, is that IntelliSense will not explicitly show that numeric values are accepted (see Figure 3-1.).

Figure 3-1. IntelliSense displaying the method signature


A better way would be to overload the Push method. With overloading, you can provide users with two versions of Push, each with a different signature. The signature of a method is determined by its parameter list. Two signatures are different when the data types or number of parameters in the parameter list are different. Example 3-5 shows how to add a new version of Push to MyStack.

Example 3-5. Overloading the Push method of MyStack
 Public Class MyStack Inherits System.Collections.Stack Public Overrides Sub Push(ByVal obj As Object) If Not IsNumeric(obj) Then Throw New Exception("Non-numeric value in Stack") End If MyBase.Push(obj) End Sub Public Overloads Sub Push(ByVal obj As Integer) MyBase.Push(obj) End Sub End Class 

Overriding Versus Overloading

So what is the difference between Overrides and Overloads? Overriding means you are changing the implementation of a method, while overloading means adding new methods with the same name but of different signatures.


The Overloads keyword in Example 3-5 specifies that a procedure is a new version of an existing procedure with the same name. In addition to providing users with a version that accepts only integers, IntelliSense will now show that the Push method has two overloaded signatures, as shown in Figure 3-2.

Figure 3-2. Push has two overloaded signatures


3.2.4. Adding a New Method

Suppose you want your new car to be available to people with special needs, such as handicapped drivers. These drivers may not be able to use the conventional brake pedals, and hence, you might need to add a special brake pedal to the steering wheel.

You can add new methods to a derived class to add functionality that's not there. For example, if you want users to push strings onto the Stack, you can define special string-friendly methods for the Push and Pop operations, as shown in Example 3-6.

VB Black Belt: Hiding a Method

Although the Overloads keyword in VB 2005 lets you define two different versions of the Push method of MyStack, because the older version is still available, it's possible to push in a non-numeric value into the Stack and cause a runtime error. A better way to prevent users from assigning a non-numeric value into the Stack would be to totally remove the original Push method that accepts the Object parameter. You can do this by using the Shadows keyword:

 Public Class MyStack Inherits System.Collections.Stack Public Shadows Sub Push(ByVal obj As Integer) MyBase.Push(obj) End Sub End Class 

The Shadows keyword will hide all other methods of the same name. In this case, the Push method now has only one signature, as confirmed by IntelliSense in the figure.


Example 3-6. Adding new Push and Pop methods to MyStack
 Public Class MyStack Inherits System.Collections.Stack Public Sub PushStr(ByVal obj As String) MyBase.Push(obj) End Sub Public Function PopStr() As String Return MyBase.Pop End Function End Class 

You can use the new methods as follows:

 Dim ms1 As New MyStack ms1.PushStr("Hello") MsgBox(ms1.PopStr()) 

3.2.5. Customizing Initialization

The purpose of a constructor is to initialize the properties of an object when it is instantiated (i.e., created).

Constructors are optional for a class.


A constructor is a subroutine with the reserved name New. You can have as many constructors as you need so long as each has a different signature. Let's add two constructors to the MyStack class, as shown in Example 3-7.

Example 3-7. Adding a custom constructor to MyStack
 Public Class MyStack Inherits System.Collections.Stack Public Sub New() '---uses the base class constructor MyBase.New() End Sub Public Sub New(ByVal items() As Object) For i As Integer = 0 To items.Length - 1 MyBase.Push(items(i)) Next ' the following will also work: ' MyBase.New(items) End Sub … 

The first constructor does not accept an input parameter, and is hence known as the default constructor (or empty constructor) of the class. The second constructor takes one parameter, items, which is then used to populate the Stack.

You can now use either of the two constructors to create an instance of the MyStack class, and the second constructor also initializes its content in a single statement, as shown in the following snippet:

 Dim itemsArray() As Object = {"Hello", "World"} Dim ms1 As New MyStack Dim ms2 As New MyStack(itemsArray) MsgBox(ms2.PopStr()) '---shows "World" MsgBox(ms2.PopStr()) '---shows "Hello" 

VB 6 Tip: The Sub New procedure in VB 2005 initializes objects when they are instantiated; it replaces the Class_ Initialize method used in VB 6 and earlier versions. Also, the Sub New procedure is called only when an object is instantiated; it cannot be called directly. The Class_Initialize event does not accept any arguments.


3.2.6. Adding Properties

The .NET Stack class exposes a number of properties, such as Count, IsSynchronized, and SyncRoot. For example, the Count property returns the number of items in the Stack. You can add additional properties to the MyStack class by using the Property keyword. For example, you might want to expose a new CountNumeric property to return the number of items in the stack that are of numeric type. You may also want to add a Description property to add a description to the class. To do so, add the code shown in bold in Example 3-8.

Example 3-8. Adding properties to MyStack
 Public Class MyStack Inherits System.Collections.Stack Private _Description as String ReadOnly Property CountNumeric() As Integer Get Dim counter As Integer = 0 For Each o As Object In Me If (IsNumeric(o)) Then counter += 1 End If Next Return counter End Get End Property Property Description() As String Get Return _Description End Get Set(ByVal value As String) _Description = value End Set End Property … 

ReadOnly and WriteOnly Properties

There are times when you want to allow users to read the values of a property only (and not set it). To do this, you use the ReadOnly keyword as a prefix to the property definition. Note that if you use this keyword, you cannot use a Set accessor block:

 ReadOnly Property CountNumeric( ) As Integer Get … … End Get End Property 

Likewise, you can make a property write-only (so that people can set its value but not read it). You can do so using the WriteOnly keyword. Likewise, you cannot have the Get accessor block if you use this keyword:

 WriteOnly Property CountNumeric( ) As Integer Set … … End Get End Property 

For a read/write property, you need both the Set and Get accessors. If you forget either one, Visual Studio 2005's new AutoCorrect feature will gladly help you to fix the missing accessor (see figure).


The Set and Get accessors that you define allow you to assign and retrieve values from the properties of a class. Notice that the values set for the Description property are stored internally in the private variable _Description.

The Private access modifier that precedes the declaration for _Description restricts the use of the variable to within the class. That is, they are not visible to code outside the class.

Using the Dim keyword within a class has the same effect as using the Private keyword. The following statements are equivalent:

 Private _Description as String ' same as Dim _Description as String 


Here's a code snippet that uses the Set property procedure to assign values to the CountNumeric and Description properties and the Get property procedure to display them:

 Dim itemsArray() As Object = {"Visual", "Basic", 2005} Dim ms1 As New MyStack(itemsArray) ms1.Description = "This is my own Stack class!" MsgBox(ms1.CountNumeric) ' displays 1 MsgBox(ms1.Description) ' displays "This is my own Stack class!" 

With…End With

You can use the new VB 2005 With…End With construct to perform a series of operations on a specified object without repeatedly typing the name of the object. For example, the above could be rewritten as:

 With ms1 MsgBox(.CountNumeric) MsgBox(.Description) End With 


VB 6 Tip: In VB 6, you can use default properties for objects. For example, you can simply assign a string to the TextBox control, like this:

 TextBox1="Hello World" ' equivalent to… TextBox1.Text="Hello World" 

This is because the Text property is the default property. The downside to using this approach is that the code is now less readable; it is much better to explicitly specify the property.

In VB 2005, default properties for controls are no longer supported.


3.2.7. Weakly Typed Versus Strongly Typed Variables

When you declare a variable to be of a certain data type, it is said to be strongly typed. For example, a variable may be declared to be of Integer type. When you declare the data type of this variable, the Visual Basic compiler performs memory allocation for the Integer data type as well as optimizations before the program is executed.

However, there are times when using a strongly typed variable isn't possible. As an example, the Push method of the Stack class accepts an item of type Object (see Figure 3-3).

Figure 3-3. Pushing an Object into a Stack


You can assign the value that you have popped from the Stack into an Object variable:

 Dim obj As Object obj = s1.Pop 

Because you can't determine what data type obj will be assigned to until runtime, in this case, obj is known as weakly typed.

The downside to using weakly typed variables is that they are less efficient (and thus slower), as doing the type conversion at runtime chews up resources. Also, a code editor feature like IntelliSense in Visual Studio will not be able to take advantage of features accorded by early binding. Figure 3-4 shows that IntelliSense does not know the properties and methods available in obj until runtime.

Figure 3-4. IntelliSense has no clue what data type obj is assigned to


However, you can perform an explicit type conversion for IntelliSense to display the properties and methods available in obj using the CType function (see Figure 3-5).

Figure 3-5. Performing an explicit type conversion using the CType function


In contrast, in the MyStack class, you have added the PushStr method, which takes in a string data type, and the PopStr method, which returns a string data type. As the type of the variable is known before the program is run, the str variable is said to be strongly typed:

 Dim ms1 As New MyStack ms1.PushStr("This is a string") Dim str As String str = ms1.PopStr 

While using strongly typed variables has its disadvantages over weakly typed variables, using a weakly typed variable is useful in cases where you are writing generic code (such as the Stack class) and won't know the type of object you are working with until runtime.

3.2.8. Using a Generic Class

With a class like Stack, which has many uses, it would be great if you could specify the data types to be handled by a particular instance at the time the Stack object is created.

VB 2005 now supports a new feature known as generics and provides a number of so-called generic classes that anticipate, by design, that they will be customized before they are instantiated. Using generics, you can define classes that let you specify the data types a class accepts when the class is instantiated.

To see the benefits of generics, let's revisit the Stack class that we have been discussing in the last few sections. Without generics, you are likely to find that you need to write multiple versions of the Stack class if you want the class to work with more than one data type say, integers, strings, or a complex Employee object. Using generics, you can now defer specifying the type of data that you want to use for your Stack until you actually instantiate a Stack object in your program. You'll find a new generic Stack class in the System.Collections.Generic namespace of the .NET Framework Class Library that allows you to specify during design time the data type you want to use. Here's how to declare that you want to push and pop integers on a stack:

 Dim s2 As New _ System.Collections.Generic.Stack(Of Integer) s2.Push(5) s2.Push(6) s2.Push("Some string…") ' error 

The System.Collections.Generic namespace contains generic versions of other data structures as well, such as List, Queue, Dictionary, and more.


By using the new Of keyword, you indicate the type of data you want to use with that class.

If you turn Option Strict On, the code editor will underline the third Push method call, indicating that it is an error to push in a string data type.


If you want to use the Stack class for String data types, you simply do this:

 Dim s2 As New _ System.Collections.Generic.Stack(Of String) s2.Push("VB2005 ") s2.Push("supports ") s2.Push("Generics") 

Besides using the generic classes in the .NET Framework, you can also write your own generic classes, a topic that is beyond the scope of this book. (For additional information, see Programming Visual Basic 2005, O'Reilly.)

3.2.9. Splitting Up the Physical Implementation of a Class

VB 2005 supports a new .NET 2.0 enhancement: partial classes. In a nutshell, with partial classes, you can now split your class definition into multiple physical files. Logically, partial classes do not make any difference to the compiler. During compile time, the Visual Basic compiler simply groups all the various partial classes together and treats them as a single entity.

Advantages of Generics

Based on what has been discussed, it's not difficult to see the following advantages of using generics:


Type safety

Generic types enforce type compliance at compile time and not runtime (as in the case of using Object). This reduces the chances of data type conflict at runtime.


Performance

The data types to be used in a generic class are determined at compile time, hence, there is no need to perform type casting at runtime, which is a computationally costly process.


Code reuse

Since you only need to write the class once and customize it to use with the various data types, there is a substantial amount of code reuse.


One of the greatest benefits of partial classes is that they allow a clean separation of business logic and the user interface (in particular, the code that is generated by the visual designer in Visual Studio 2005).

Using partial classes, the UI code can be hidden from the developer, who usually has no need to access it anyway. Partial classes also make debugging easier, as the code is partitioned into separate files.

Reasons to Use Partial Classes

Partial classes allow programmers on your team to work on different parts of a class without needing to share the same physical file. While this is useful for projects that involve big class files, be wary: if you find your class files getting too large, it may well signal a design fault, and refactoring may be required.

The most compelling reason for using partial classes is to separate your application business logic from the designer-generated code. For example, the code generated by Visual Studio 2005 for a Windows Form is kept separate from your business logic. This will prevent developers from messing with the code that is used for the UI. At the same time, it will prevent you from losing your changes to the designer-generated code when you change the UI.


Figure 3-6 shows the code behind of a Windows Form: Form1. Notice that no hidden Windows designer-generated code appears on the page (as would be visible in Visual Studio .NET 2003). The absence of that section allows you to concentrate on writing the business logic of your application and reduces the chances that you may inadvertently modify the code generated by the designer.

Figure 3-6. The code behind of Form1


If for some reason you need to access the Windows designer-generated code, you can go to Solution Explorer and click on the Show All Files button. There you will find a file named Form1.Designer.vb (see Figure 3-7) under the Form1.vb file.

Figure 3-7. Revealing the Windows designer-generated code in Solution Explorer


Figure 3-8 shows the content of the Form1.Designer.vb file.

In VB 2005, all of the partial classes except one must use the Partial keyword prefix; only one class may omit it. However, it is recommended that you always prefix all your partial classes with the Partial keyword. At least this will give you a visual clue that part of the implementation of the class lies somewhere else, and this is definitely useful when it comes to debugging.

Figure 3-8. The content of the Windows designer-generated code


While partial classes allow you to split the definition of a class into multiple files, you cannot mix languages. That is, all partial classes must be written in the same language. Besides using the Partial keyword for classes, you can also use it for structures and interfaces.


If your class implements many interfaces (see "Creating Contracts with Implementers Using Interfaces" for more details on interfaces), it is a good idea to use a partial class to contain the implementation for each interface.



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

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