.NET Generics

An Introduction

asp:cover story

LANGUAGES: VB.NET

ASP.NET VERSIONS: 2.0

 

.NET Generics

An Introduction

 

By Vikram Srivatsa

 

The next technology wave from Microsoft is going to sweep developers the world over in the form of the .NET Framework 2.0. Still in its first beta, the 2.0 release of .NET is going be a major overhaul to the framework and is comprised of various new features and innovations. One such new innovation is the concept of generics. This article introduces the concept of generics, the problem space that it addresses, and the application of generics. Along the way we ll transform this new feature from a buzzword to a real-world concept. (This article is based on Beta 1 of the .NET Framework 2.0, Build 2.0.40607.)

 

Generics: A Primer

To understand a concept, it is important to first define the concept in precise terms. The framework designers define generics as follows: Generics is a feature that permits classes, structures, interfaces, delegates, and methods to be parameterized by the types of data they store and manipulate.

 

The goal of programming has always been to implement some kind of business logic that acts on data. Invariably this data is usually represented as belonging to a specific data type. Hence, it becomes necessary to write separate methods, structures, etc. to overcome this problem of type specificity. The other alternative would be to use a generic data type in such scenarios as System.Object and then cast as required to specific data types.

 

For example, if we consider a simple class, such as Car, and assume that the class has properties such as Height, Weight, and Length (Visual Basic.NET is used in all the code samples and all examples assume that the code is written having the Option Strict option set to on):

 

Public Class Car

 Private intHeight As Integer

 Private intWeight As Integer

 Private intLength As Integer

End Class

 

Now suppose we want to increase the efficiency and in some scenario utilize decimal values for these three properties; we would need to make use of a different data type and write a class based on the data type. If we were to choose this approach, the above example Car class would appear as follows:

 

Public Class Car

 Private dblHeight As Double

 Private dblWeight As Double

 Private dblLength As Double

End Class

 

This approach leads to issues with maintainability because we are duplicating code to support different data types. The better approach is to use a generic data type such as System.Object to represent the properties. In that approach, the same code would appear as shown here:

 

Public Class Car

 Private Height As Object

 Private Weight As Object

 Private Length As Object

End Class

 

In a way this approach solves the problem of maintainability because we only have one, single implementation. However, this approach does not provide type safety and definitely adds the overhead of casting.

 

To gain a better and more practical understanding of this issue, let s take a look at another example. Let s look at a small code example that involves the Hashtable class from the System.Collections namespace (see Figure 1). The Hashtable class has been implemented making use of an object for storing data, thus providing the ability to store any data in it.

 

Public Sub MyMethod()

 'Declaration of Customer Classes and a Hashtable

 Dim htDataStore As New Hashtable()

 Dim objNewCustomer As New Customer()

 Dim objOldCustomer As New Customer()

 Try

 'Set the Property on Customer Object and Add to Hashtable

 objNewCustomer.Name = "New Customer"

 htDataStore.Add("KEY1", objNewCustomer)

 'Retrieve the same object back from Hashtable by

 'casting it back

 objOldCustomer = CType(Convert.ChangeType(

 htDataStore("KEY1"), objOldCustomer.GetType()), Customer)

 Catch ex As Exception

  Console.WriteLine("EXCEPTION:" + ex.Message)

 End Try

End Sub

Figure 1: The working of a typical collection class, the Hashtable.

 

The code snippet in Figure 1 demonstrates the working of a typical collection class, the Hashtable. The Customer objects are stored in the Hashtable based on a key value. To retrieve this Customer object, the object retrieved based on the key value has to be cast back to its original type. Figure 2 below illustrates this casting more clearly.

 


Figure 2: Storage of Customer objects in a Hashtable.

 

The main drawback here is that the Hashtable only exposes Object types, which creates the potential for a lot of invalid casts at run time. The .NET Framework 2.0 addresses this problem by making use of generics, which allows for storing strongly typed objects in a collection.

 

Because generics allows the type information to be specified by the code that utilizes the generics Type, this allows for making use of common algorithms and eliminates the need to duplicate code in order to support multiple types. This behavior is called parametric polymorphism, which the .NET Framework 2.0 supports via generics.

 

Generics in the .NET Framework

Let s delve deeper to understand the generics implementation in the .NET Framework. The generic collection classes are included as part of a new namespace known as System.Collections.Generic . The framework also allows for developers to write their own generic classes, as well.

 

Before we look at the various generic classes provided by the .NET Framework, let s rewrite the example from Figure 1 using the .NET Framework 2.0 to contrast and compare the advantages of generics (see Figure 3).

 

Public Sub MyGenericMethod()

 'Declaration of Customer Classes and a Dictionary

 Dim dctDataStore As New Dictionary(Of String, Customer)

 Dim objNewCustomer As New Customer()

 Dim objOldCustomer As New Customer()

 Try

  'Set the Property on Customer Object and Add

  'to Dictionary

  objNewCustomer.Name = "New Customer"

  dctDataStore.Add("KEY1", objNewCustomer)

  'Retrieve the same object back from Dictionary

  objOldCustomer = dctDataStore("KEY1")

 Catch ex As Exception

  Console.WriteLine("EXCEPTION:" + ex.Message)

 End Try

End Sub

Figure 3: The example from Figure 1 revamped using the .NET Framework 2.0 to illustrate generics.

 

The Hashtable used in Figure 1 is replaced by its generic equivalent, the Dictionary class. This class takes two parameters and lets the developer define the data types of the data that the Dictionary class would be storing and manipulating.

 

When the Add method is called and the Customer is stored in the Dictionary class, the data type associated with the data can be specified. The main benefit here is that run-time errors are prevented because the compiler will check that only Customer objects are stored in the Dictionary class.

 

In case an attempt is made to pass an object of a different type, say for example a dataset was passed instead of a Customer object, the compiler would report a type conversion error and the code would not compile.

 

The key line to notice is the syntax for the usage of generic types, as shown below:

 

Dim dctDataStore As New Dictionary(Of String, Customer)

 

The Of keyword is provided in VB.NET to specify a data type for the generic type.

 

The .NET Framework has added to the System.Collections.Generic namespace equivalents for most of the classes that were present in the System.Collections namespace (see the table in Figure 4).

 

System.Collections Namespace

System.Collections.Generic Namespace

HashTable

Dictionary

ArrayList

List

Queue

Queue

Stack

Stack

Comparer

Comparer

ICollection

ICollection

IComparable

IComparable

IComparer

IComparer

IDictionary

IDictionary

IList

IList

SortedList

SortedDictionary

Figure 4: System.Collections.Generic namespace equivalents.

 

Internals

.NET 2.0 supports generics at the Intermediate Language level. The implementation of generics has been possible through changes to the Intermediate Language instructions, as well as changes in metadata, the JIT compiler, and the various language compilers. Also, because generics is supported at the IL level, it can work across language barriers. This means that a generic type defined in one managed language, such as VB.NET, can be utilized in another managed language, such as C#.

 

In other languages, such as C++ templates or Java generics, the generics feature is one that is implemented based on compile-time code construction. This means there is code bloat in these implementations. The .NET implementation is more elegant in the sense that generics implementation is based on run-time expansions instead of compile time.

 

The compiler is capable of generating IL, which has placeholders for the actual specific types; hence, there is no code bloat. Furthermore, the metadata would contain information regarding the generic type. This metadata is used to provide compile-time checks, type safety, and IntelliSense. Figure 5 depicts this flow to make it easier to understand.

 


Figure 5: A generic type defined in code is compiled into generic IL and metadata.

 

Constraints

Because generics are compiled into generic IL without any specific type information, it is possible for generic code to try to make use of certain types that are incompatible with the specific types for which it is meant to be used. To provide a check against such scenarios, the CLR generics implementation provides for constraints. The constraints are an additional feature provided to authors of generic classes to maintain type safety, and are optional.

 

There are two types of constraints that can be specified with generics: derivation and constructor.

 

Derivation constraint. A derivation constraint can be defined to impose a restriction that the parameter to the generic type must implement a specific interface or must derive from a particular base class. The following code snippet shows a sample generic type making use of a derivation constraint:

 

Public Class MySampleClass(Of K, T As IComparable)

End Class

 

The above generic type defines that the second parameter (T) must implement the interface IComparable. Again, the language compiler can make use of this and provides type safety and compile-time checks.

 

Constructor constraint. A constructor constraint can be defined to impose a restriction that the generic type should provide a default public constructor that does not accept any parameters. This is also known as the New constraint. The following code snippet shows a sample generic type with a constructor constraint specified:

 

Public Class MySampleClass2(Of T As New)

 Public Sub MySampleClass2()

 End Sub

End Class

 

It is also possible to combine and specify both types of constraints on the same generic type. The code snippet below demonstrates this concept:

 

Public Class MySampleClass2(Of T As New, IComparable)

 Public Sub MySampleClass2()

 End Sub

End Class

 

Thus, the .NET implementation of generics offers control to class designers to provide an additional level of type safety by applying constraints on the custom generic types that they define.

 

Inheritance and Generic Classes

Let s move on to see how these generic classes can be inherited. The generic classes allow for inheritance like other classes. The following example shows a custom MyStack class inheriting from the .NET Framework generic class Stack(Of T); the subclass can specify the specific type being used if the type need not be generic in the derived class:

 

Public Class MyStack

 Inherits Stack(Of Int32)

End Class

 

In case it is needed for the subclass to also support a generic type, then the following definition can be used:

 

Public Class MyStack2(Of T)

 Inherits Stack(Of T)

End Class

 

Generic Methods

The concept of generics is extended to methods in the .NET Framework 2.0. This means that the concept of generics provides a facility by which a method can have generic parameters and can be called with a different type each time. The code snippet in Figure 6 demonstrates defining and using generic methods.

 

Public Class MyTestClass

 Public Sub MyTestClass()

 End Sub

 'Generic Method accepting a parameter K

 Public Sub MyGenericMethodA(Of K)(ByVal ParamVar As K)

 End Sub

 'Generic Method accepting a Parameter K and also

 'returning a Generic Type

 Public Function MyGenericMethodB(Of K)(

 ByVal ParamVar As K) As K

 Dim ReturnValue As K

 'Add Function Specific Code

 Return (ReturnValue)

 End Function

 Public Sub MyMethod()

 Dim objTestMyClass As New MyTestClass()

 Dim intValue As Int32

 Dim strValue As String

 'Code Snippet Showing Generic Method in use.

 objTestMyClass.MyGenericMethodA(Of String)("Hello World")

 objTestMyClass.MyGenericMethodA(Of Int32)(4)

 'Code Snippet Showing Generic Method in use.

 strValue = objTestMyClass.MyGenericMethodB(

  Of String)("Hello World")

 intValue = objTestMyClass.MyGenericMethodB(Of Int32)(35)

 End Sub

End Class

Figure 6: Defining and using generic methods.

 

In the class MyTestClass, the methods MyGenericMethodA and MyGenericMethodB accept generic parameters. It is possible to call these methods at run time by specifying a different type. This is also demonstrated within the method MyMethod in Figure 6.

 

Shared methods are also supported, and it is possible to overload operators such as + or - to work with generic types (operator overloading is supported in Visual Basic.NET 2005). The concept of generics can also be used with delegates, as well as structures.

 

Generic Structures

Let s take a look at an example of generics being applied to structures. A common example for a generic structure is one that represents a Point:

 

Structure Point(Of T)

  Dim XCoordinate As T

  Dim YCoordinate As T

End Structure

 

This provides an implementation of the Point structure, irrespective of the type. The client code can specify the type and can have the Point structure either make use of integer values or double values. The code snippet below shows a method that declares the Point class first as an integer implementation, then as a double implementation:

 

Public Sub UseStruct()

 'Integer Implementation

 Dim objPoint As Point(Of Int32)

 objPoint.XCoordinate = 100

 objPoint.YCoordinate = 100

 'Double Implementation

 Dim objDoublePoint As Point(Of Double)

 objDoublePoint.XCoordinate = 100.01

 objDoublePoint.YCoordinate = 100.01

End Sub

This clearly eliminates the need for duplicating code in order to support multiple types.

 

Support across the Framework

In this section we ll take a look at some other areas of the .NET Framework in which generics can be used. We ll also list some areas in which generics are not supported.

 

Generics support reflection, serialization, and .NET Remoting. It is possible to use reflection to query generic types. Generics do not support Web services. That is, it is not possible to have Web methods that make use of generic types. It is also not possible to have serviced components that make use of generics or to have attributes that are generic in nature.

 

Conclusion

In this article I introduced generics, a powerful new concept in the .NET Framework 2.0. I covered the need for generics and dug a little deeper into the internal implementation details of generics. I also mentioned the various generic classes provided by the .NET Framework.

 

I demonstrated with code samples the concept of generics as applied to classes, methods, and structures. Finally, I discussed the impact of generics on various technologies spread across the framework.

 

Generics represents a big step toward providing ways to isolate data-specific algorithms from being type specific. With a clean and elegant implementation and support in the IDE in terms of IntelliSense, .NET generics is all set to capture the minds of developers across the world.

 

Vikram Srivatsa is a senior software engineer working with Hewlett-Packard GDIC in Bangalore, India, and has been a key developer on various development projects for the Microsoft Technology Practice in the company. He has been associated with the .NET Framework since the Beta stages of version 1.0. Vikram is an MCSD in .NET and a Sun Certified Java Programmer. His other qualifications include an engineering degree in Computer Science.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish