GENERICS IN .NET 2.0
Posted On June 30, 2007 by Madhu AP filed under Internet
The type system of a programming language enables us to check for illegal instructions and memory violations at compile time. Defining the types for values at design time allows us to detect related bugs in the code at compile time in all the strongly typed languages, including C# and VB.net (using Option Explicit On). Since the binding is done early, it makes the code run faster and thus increases its performance. In spite of all these advantages, type system has some disadvantages. It reduces flexibility of the code and restricts its reusability. If the same type of operations is to be executed on two different types of values, we need to rewrite the code for each type. For example, if we need to write stack classes for String and Integer values, we will have to write two classes for it, as in code 1:
Code 1
For Integer Values:
Public Class PopUpIntegers
Dim index As Int64
Dim Values() As Int64
Public Sub New(ByVal Size As Int64)
ReDim Preserve Values(Size)
index = -1
End Sub
Public Sub push(ByVal Value As Int64)
If Not (index + 1) > Values.GetUpperBound(0) Then
index += 1
Values(index) = Value
End If
End Sub
Public Function pop() As Int64
If Not index < 0 Then
index -= 1
Return Values(index + 1)
End If
End Function
End Class
For String Values:
Public Class PopUpStrings
Dim index As Int64
Dim Values() As String
Public Sub New(ByVal Size As Int64)
ReDim Preserve Values(Size)
index = -1
End Sub
Public Sub push(ByVal Value As String)
If Not (index + 1) > Values.GetUpperBound(0) Then
index += 1
Values(index) = Value
End If
End Sub
Public Function pop() As String
If Not index < 0 Then
index -= 1
Return Values(index + 1)
End If
End Function
End Class
The above classes are type-safe classes. If we assign any illegal values, the compiler will catch them at compile time. For example, if we write the following statements in the consumer code, they will raise run-time errors.
Dim objPopUpIntegers As New PopUpIntegers(10)
objPopUpIntegers.push("huh") ‘COMPILE TIME ERROR
Dim strOutPut As String
strOutPut = objPopUpIntegers.pop() ‘COMPILE TIME ERROR
In the first case, we are assigning a string value to the Integer type array element while in the second case the integer values are assigned to a string variable. The compiler detects these illegal statements at compile time. Although the code is type-safe and will run faster because the data types are determined at compile time, this is at the expense of writing the same code twice for two different types of values. This could have been overcome by using the System.Object type, which can hold the values of all primitive types. Check out code 2.
Code 2
Public Class PopUpObjects
Dim index As Int64
Dim Values() As Object
Public Sub New(ByVal Size As Int64)
ReDim Preserve Values(Size)
index = -1
End Sub
Public Sub push (ByVal Value As Object)
If Not (index + 1) > Values.GetUpperBound(0) Then
index += 1
Values(index) = Value
End If
End Sub
Public Function pop() As Object
If Not index < 0 Then
index -= 1
Return Values(index + 1)
End If
End Function
End Class
If we use the above class in the consumer code, we cannot catch the wrong assignments at compile time. There is no type safety. We can pass any value in the push method. That means, all the following statements are valid in the consumer code:
Dim objPopUpIntegers As New PopUpObjects(10)
objPopUpObjects.push("huh")
objPopUpObjects.push(10)
objPopUpObjects.push(AnyObject)
When we assign values returned by the pop method of the class object, we need to do the casting. If we assign values without casting the objects into the primitive types, they will raise compile time bugs. Thus, the following statements are invalid in the consumer code:
Dim strOutPut As String
strOutPut = objPopUpIntegers.pop() ‘COMPILE TIME ERROR
Dim IntOutPut As Integer
IntOutPut = objPopUpIntegers.pop() ‘COMPILE TIME ERROR
These statements need to be written in the consumer code as follows:
Dim strOutPut As String
strOutPut = Convert.toString(objPopUpIntegers.pop())
Dim IntOutPut As Integer
IntOutPut = Convert.toInt32(objPopUpIntegers.pop())
However, the problem in the above code is performance. When any value of the primitive type is assigned to an object, it involves boxing. That means, memory allocation, memory copy and garbage collection. In the reverse process, when the object is cast to any of the primitive type, unboxing is done, which includes type checking. This boxing and unboxing reduces performance of the code. So, if performance is the primary concern, you may not opt for the code that uses objects as the data types.
As a solution to the above problem, Microsoft has added a cool feature known as Generics to .NET 2.0. Actually, Generics is the parametric polymorphism that is supported by many programming languages but has not been available in .NET languages till now. In .NET, Generics is the type extension of the Common Language Runtime (CLR). Using generics, we can write code that is type-safe, runs faster and works with any data type. As the CLR supports many languages, you can write the generic code in any .NET language. Depending on the language, syntax of the generic code will also change, however, they are absolutely inter-usable with each other.
Let us rewrite the above PopUp class using Generics, which Microsoft has made available out of the box in Whidbey. Refer code 3.
Code 3
Public Class PopUpGenerics(Of T)
Dim index As Int64
Dim Values() As T
Public Sub New(ByVal Size As Int64)
ReDim Preserve Values(Size)
index = -1
End Sub
Public Sub push(ByVal Value As T)
If Not (index + 1) > Values.GetUpperBound(0) Then
index += 1
Values(index) = Value
End If
End Sub
Public Function pop() As T
If Not index < 0 Then
index -= 1
Return Values(index + 1)
End If
End Function
End Class
In generics, we specify the data type as the replaceable element. This missing information is completed in the consumer code using the (of T) construct. The capital “T” is used by convention and can be replaced by any letter. Let us take a look at how this class will be used in the consumer code. See code 4.
Code 4
For Integer Values:
Dim objPopUpGenerics As New PopUpGenerics(Of Integer)(10)
objPopUpObjects.push(100)
objPopUpObjects.push(200)
Dim intOutPut As Integer
intOutPut = objPopUpObjects.pop()
Dim strOutPut As String
strOutPut = objPopUpObjects.pop() ‘COMPILE TIME ERROR
For String Values:
Dim objPopUpGenerics As New PopUpGenerics(Of String)(10)
objPopUpObjects.push(“huh”)
objPopUpObjects.push(“haha”)
Dim strOutPut As String
strOutPut = objPopUpObjects.pop()
Dim intOutPut As Integer
intOutPut = objPopUpObjects.pop() ‘COMPILE TIME ERROR
When we define the class with (Of Integer), it uses the Integer type wherever we have specified the “T” type and, similarly, when we define the class with (Of String), it uses the String type wherever we have specified the “T” type in the class. As the data types of generic classes are determined at compile time, it reduces the chances of illegal assignments. There is no need of casting, which increases the performance of the code. As the class works with all data types, there is a substantial increase in the code reuse.
Not only can Generics be used at the class level, we can also write generic code at the procedure level. Let us write a procedure in VB.NET, which uses the generics feature of .NET 2.0.
Public Sub Swap(Of T)(ByRef Val1 As T, ByRef Val2 As T)
Dim tmp As T
tmp = Val1
Val1 = Val2
Val2 = tmp
End Sub
In case we would like to use the above procedure for integer values, we will call the procedure in the consumer code as:
Dim j, k As Integer
j = 10
k = 100
Swap(Of Integer)(j, k)
Similarly, in case of String values, we will write the calling code as:
Dim str1, str2 As String
str1 = "Sajad"
str2 = "Ahmad"
Swap(Of String)(str1, str2)
The generics in .NET can be compared with templates in C++, but there is a lot of difference between these two, especially in implementation. Generics is not based on the traditional design of compile and link but is implemented deep in the CLR, which has resulted in the high performance of generic code written in .NET. Using generics, we can write type-safe code in .NET that can run with any data type, thus increasing its reusability and flexibility.

Sajad Deyargarooo is a software developer working for BQE Software Inc., United States of America.
