DEFINING GENERICS

Visual Basic allows you to define generic classes, structures, interfaces, procedures, and delegates. The basic syntax for all of those is similar, so once you know how to make generic classes, making generic structures, interfaces, and the others is fairly easy.

To define a generic class, make a class declaration as usual. After the class name, add a parenthesis, the keyword Of, a placeholder for a data type, and a closing parenthesis. The data type placeholder is similar to a parameter name that you would give to a subroutine except it is a type, not a simple value. The class’s code can use the name ItemType to refer to the type associated with the instance of the generic class.

For example, suppose you want to build a binary tree that could hold any kind of data. The following code shows how you could define a BinaryNode class to hold the tree’s data:

Public Class BinaryNode(Of T)
    Public Value As T
    Public LeftChild As BinaryNode(Of T)
    Public RightChild As BinaryNode(Of T)
End Class
 

The class’s declaration takes a type parameter named T. (Many developers use the name T for the type parameter. If the class takes more than one type parameter separated by commas, they start each name with T as in TKey and TData.)

The class defines a public field named Data that has type T. This is where the node’s data is stored.

The class also defines two fields that refer to the node’s left and right children in the binary tree. Those fields hold references to objects from this same class: BinaryNode(Of T).


GENERIC CLASSES AND METHODS
The Switcher class is not generic but it contains a generic method. Note that a generic class can also contain generic and non-generic methods. You can also create generic methods in code modules.

The following code shows how a program could use this class to build a small binary tree of Employee objects:

' Define the tree's root node.
Dim root As New BinaryNode(Of Employee)
root.Value = New Employee("Ben", "Baker")
 
' Create the root's left child.
root.LeftChild = New BinaryNode(Of Employee)
root.LeftChild.Value = New Employee("Ann", "Archer")
 
' Create the root's right child.
root.RightChild = New BinaryNode(Of Employee)
root.RightChild.Value = New Employee("Cindy", "Carter")
 

Generic Constructors

Generic classes can have constructors just as any other class can. For example, the following constructor initializes a BinaryNode object’s LeftChild and RightChild references:

' Assign this node's left and right children.
Public Sub New(new_value As T,
 Optional left_child As BinaryNode(Of T) = Nothing,
 Optional right_child As BinaryNode(Of T) = Nothing)
    Value = new_value
    LeftChild = left_child
    RightChild = right_child
End Sub
 

To use the constructor, the main program adds normal parameters after the type parameters in the object declaration. The following code uses the new constructor to create a binary tree similar to the previous one:

' Define the child nodes.
Dim left_child As New BinaryNode(Of Employee)(New Employee("Ann", "Archer"))
Dim right_child As New BinaryNode(Of Employee)(New Employee("Cindy", "Carter"))
 
' Define the tree's root node.
Dim root As New BinaryNode(Of Employee)(
    New Employee("Ben", "Baker"),
    left_child, right_child)
 

Multiple Types

If you want the class to work with more than one type, you can add other types to the declaration separated by commas. For example, suppose that you want to create a dictionary that associates keys with pairs of data items. Example program GenericPairDictionary uses the following code to define the generic PairDictionary class. This class acts as a dictionary that associates a key value with a pair of data values. The class declaration includes three data types named TKey, TValue1, and TValue2.

' A Dictionary that associates a pair of data values with each key.
Public Class PairDictionary(Of TKey, TValue1, TValue2)
    ' A structure to hold paired data.
    Private Structure ValuePair
        Public Value1 As TValue1
        Public Value2 As TValue2
        Public Sub New(new_value1 As TValue1, new_value2 As TValue2)
            Value1 = new_value1
            Value2 = new_value2
        End Sub
    End Structure
 
    ' A Dictionary to hold the paired data.
    Private ValueDictionary As New Dictionary(Of TKey, ValuePair)
 
    ' Return the number of data pairs.
    Public ReadOnly Property Count() As Integer
        Get
            Return ValueDictionary.Count
        End Get
    End Property
 
    ' Add a key and value pair.
    Public Sub Add(ByVal key As TKey,
     new_value1 As TValue1,
     new_value2 As TValue2)
        ValueDictionary.Add(key, New ValuePair(new_value1, new_value2))
    End Sub
 
    ' Remove all data.
    Public Sub Clear()
        ValueDictionary.Clear()
    End Sub
 
    ' Return True if PairDictionary contains this key.
    Public Function ContainsKey(key As TKey) As Boolean
        Return ValueDictionary.ContainsKey(key)
    End Function
 
    ' Return a data pair.
    Public Sub GetItem(ByVal key As TKey,
     ByRef data_value1 As TValue1,
     ByRef data_value2 As TValue2)
       Dim data_ pair As DataPair = ValueDictionary.Item(key)
       data_value1 = data_ pair.Value1
       data_value2 = data_ pair.Value2
    End Sub
 
    ' Set a data pair.
    Public Sub SetItem(key As TKey,
     data_value1 As TValue1,
     data_value2 As TValue2)
        ValueDictionary.Item(key) = New DataPair(data_value1, data_value2)
    End Sub
 
    ' Return a collection containing the keys.
    Public ReadOnly Property Keys() As System.Collections.ICollection
        Get
            Return ValueDictionary.Keys()
        End Get
    End Property
 
    ' Remove a particular entry.
        Public Sub Remove(key As TKey) ValueDictionary.Remove(key)
    End Sub
End Class
 

The PairDictionary class defines its own private ValuePair class to hold pairs of data values. The ValuePair class has two public variables of types TValue1 and TValue2. Its only method is a constructor that makes initializing the two variables easier.

After defining the ValuePair class, the PairDictionary class declares a generic Dictionary object named ValueDictionary using the key type TKey and data type ValuePair.

PairDictionary provides Count, Add, Clear, ContainsKey, GetItem, SetItem, Keys, and Remove methods. Notice how it delegates these to the ValueDictionary object and how it uses the ValuePair class to store values in ValueDictionary.

The following code creates an instance of the generic PairDictionary class that uses integers as keys and strings for both data values. It adds three entries to the PairDictionary and then retrieves and displays the entry with key value 32.

' Create the PairDictionary and add some data.
Dim pair_dictionary As New PairDictionary(Of Integer, String, String)
pair_dictionary.Add(10, "Ann", "Archer")
pair_dictionary.Add(32, "Bill", "Beach")
pair_dictionary.Add(17, "Cynthia", "Campos")
 
' Print the values for index 32.
Dim value1 As String = ""
Dim value2 As String = ""
pair_dictionary.GetItem(32, value1, value2)
Debug.WriteLine(value1 & ", " & value2)
 

Constrained Types

To get the most out of your generic classes, you should make them as flexible as possible. Depending on what the class will do, however, you may need to constrain the class’s generic types.

For example, suppose you want to make a generic SortedBinaryNode class similar to the BinaryNode class described earlier but that keeps its values sorted. When you add a new value to a node, the program compares the new value to the node’s value and places the new node in the left or right subtree depending on whether the new value is less than or greater than the node’s value.

For example, suppose node A contains the value 7 and you want to add the value 5 to its subtree. The new value 5 is less than 7 so node A would put the new value in its left subtree. If node A has no left child node then it places the new value in a new node and makes that node its left child. If node A already has a left child, Node A calls that child’s Add method to add the child somewhere in its subtree.

Figuring out whether a new value belongs in a node’s left or right subtree is relatively straightforward if the node holds integers or strings, but there’s no obvious way to determine whether one Employee object should be placed before another. The SortedBinaryNode class will only work if the data type of the objects it holds supports comparison.

One way to allow the nodes to compare items is to require that the items they contain implement the IComparable interface. Then the program can use their CompareTo methods to see whether one item is greater than or less than another item.

To require that a generic class’s type implements an interface, add an “As inferface” clause after the type’s declaration. Example program SortedBinaryTree uses the following SortedBinaryNode class:

Public Class SortedBinaryNode(Of T As IComparable)
    Public Value As T
    Public LeftChild As SortedBinaryNode(Of T)
    Public RightChild As SortedBinaryNode(Of T)
 
    Public Sub New(new_value As T)
        Value = new_value
    End Sub
 
    ' Add a new value to this node's subtree.
    Public Sub Add(new_value As T)
        ' See if it belongs in the left or right child's subtree.
        If (new_value.CompareTo(Value) < 0) Then
            ' Left subtree.
            If (LeftChild Is Nothing) Then
                ' Add it as the new left child.
                LeftChild = New SortedBinaryNode(Of T)(new_value)
            Else
                ' Add it in the left subtree.
                LeftChild.Add(new_value)
            End If
        Else
            ' Right subtree.
            If (RightChild Is Nothing) Then
                ' Add it as the new right child.
                RightChild = New SortedBinaryNode(Of T)(new_value)
            Else
                ' Add it in the right subtree.
                RightChild.Add(new_value)
            End If
        End If
    End Sub
End Class
 

A type’s As clause can specify any number of interfaces and at most one class from which the type must be derived. It can also include the keyword New to indicate that the type used must provide a constructor that takes no parameters. If you include more than one constraint, the constraints should be separated by commas and enclosed in brackets.

The following code defines the StrangeGeneric class that takes three type parameters. The first type must implement the IComparable interface and must provide an empty constructor. The second type has no constraints, and the third type must be a class that inherits from Control.

Public Class StrangeGeneric(Of T1 As {IComparable, New}, T2, T3 As Control)
    ...
End Class
 

The following code declares an instance of the StrangeGeneric class:

Dim strange_generic As New StrangeGeneric(Of Integer, Employee, Button)
 

Constraining a type gives Visual Basic more information about that type, so it lets you use the properties and methods defined by the type. In the previous code, for example, if a variable is of type T3, then Visual Basic knows that it inherits from the Control class, so you can use Control properties and methods such as Anchor, BackColor, and Font.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.15.78