Go for the Generic brand…
Before .Net 2.0 the only way that you could use a generic type was to use System.Object. Although it’s been 4 years since the release of .Net 2.0 I still see developers using the System.Object way too often, which leads to unnecessary boxing/unboxing and casting. So in this article I’m going to explain how you can use Generics in your classes and methods.
What are Generics?
I entitled this article “Go for the Generic brand” because outside the world of programming the term generic means something that is not tied to a particular brand name. You may buy a bottle of shampoo without it having one of the more recognizable names printed on its label but you can still use it and expect it to clean your hair. Generics in programming works in a similar manner, you can refer to a class without it being related to a particular Type, but we can still use it in a type-safe manner.
However unlike generic brands of merchandises, that often are thought of as inferior to well known household brands, Generics in .Net are usually a good thing. I said usually a good thing because you should not look at generics as a way to replace other techniques such as method overloading, but rather as a way to replace the usage of System.Object.
A quick example
Below is a small example on what a generic class could look like.
Public Class MyGenericClass(Of T)
Private _value As T
Public Property Value() As T
Get
Return _value
End Get
Set(ByVal value As T)
_value = value
End Set
End Property
End Class
The (Of T) part after the class name tells the compiler that this is a generic class. The T itself is merely a placeholder of a type, any type. The name T doesn’t really matter, you could just as well have typed (Of MyType) if you wanted to, however it has become a general convention to just use the capital T, unless the class expects more than one type.
In C# the same class would look like this:
public class MyGenericClass<T>
{
private T _value;
public T Value
{
get{ return _value; }
set{ _value = value;}
}
}
C# uses the angle brackets instead of the Of keyword as VB does, but other than that they work exactly in the same manner.
So let’s see how this class can be used. When we create a new object from the class we must provide the type we want to use.
Dim myString As New MyGenericClass(Of String)
Dim myInteger As New MyGenericClass(Of Integer)
myString.Value = "Hello World"
myInteger.Value = 123
Console.WriteLine(myString.Value)
Console.WriteLine(myInteger.Value)
Here I create two instances of the MyGenericClass one of the String type and the other of the Integer type. The Value property will then become of that particular type, so trying to do the following would create a compile error (or a runtime error if you have Option Strict set to Off) since I’m trying to assign a string value to an integer type.
myInteger.Value = "Hello"
This generic class is in no way restricted to only primitives and strings, you could use it with any type. Below I use a StringBuilder.
Dim myStringBuilder As New MyGenericClass(Of StringBuilder)
myStringBuilder.Value = New StringBuilder
myStringBuilder.Value.AppendLine("A string")
myStringBuilder.Value.AppendLine("Another string")
Console.WriteLine(myStringBuilder.Value)
Generic Methods
The whole class doesn’t have to use generics, you can use generics on a single method as well.
Public Function CreateArray(Of T)(ByVal size As Integer) As T()
Dim array(size) As T
Return array
End Function
Public Sub Main()
Dim myIntArray As Integer() = CreateArray(Of Integer)(5)
Dim myStringArray As String() = CreateArray(Of String)(5)
End Sub
This can be especially useful for
extension methods, when you want to add a generic method to any type.
Type constraints
Even though the code in a generic type definition should be as type-independent as possible it is sometimes useful to add some constraints to limit the number of types that are possible. A good example is if you for example need to compare items to be able to sort them in which case they must implement the IComparable interface.
Public Class MyComparer(Of T As IComparable)
'Insert code...
End Class
Apart from interfaces the type of constraints you can use are:
- The type must be of a type of, or inherit from, a class.
- The type must expose a parameterless constructor.
- The type must be a reference type, or it must be a value type.
Let’s have a look at the first, the the type must be of, or inherit from, a specified class. Let’s say you have the following classes:
Public Class Identity
Private _id As Integer
Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
End Class
Public Class Person
Inherits Identity
Private _firstName As String
Public Property FirstName() As String
Get
Return _firstName
End Get
Set(ByVal value As String)
_firstName = value
End Set
End Property
Private _lastName As String
Public Property LastName() As String
Get
Return _lastName
End Get
Set(ByVal value As String)
_lastName = value
End Set
End Property
End Class
Public Class Company
Inherits Identity
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Private _city As String
Public Property City() As String
Get
Return _city
End Get
Set(ByVal value As String)
_city = value
End Set
End Property
'more properties goes here...
End Class
So basically we here have an Identity class that expose an ID property and then we have a Person and a Company class that both inherit from the Identity class. Now let’s say that you want to create a collection class that only accepts objects of the Identity class or one of its descendants, it could then look something like the following.
Public Class IdentityCollection(Of T As Identity)
Inherits List(Of T)
End Class
You can specify more than one constraint if you like, in which case you list the different constraints inside curly braces.
Public Class IdentityCollection(Of T As {Identity, IComparable})
Inherits List(Of T)
End Class
To require an accessible parameterless constructor you would include the New keyword in the list. For a reference type include the Class keyword, and for value types you would include the Structure keyword.
Generic collections
Generics are mostly used with collections, and the .Net team have included a bunch of various generic collections for us, including list, stack, queue, and dictionary collections. They are all part of the System.Collections.Generic namespace.
In .Net 1.1 we where more or less forced to use the ArrayList collection type. However that excepted items of the System.Object type which means that casting and, if you use value types, boxing/unboxing needs to be performed. That can be very inefficient, especially in For Each loops. Using the List(Of T) we don’t have to do any of that.
Dim myList As New List(Of Person)
Conclusion
So in this article I’ve covered most of what you need to know about using generics. If you haven’t started using generics yet, I hope this article will get you started.
Have fun.