In my last blog http://blogs.interknowlogy.com/adamcalderon/archive/2005/05/22/153.aspx I talked about Generics and centered my talk on how they help you be type safe and reduce boxing. My examples illustrated how to create a collection class and brought you through the processes until we reached using a built in Generic collection class in the BCL. This introduction met the goal of getting someone up to speed on Generics and showed a soon to be very popular use of Generics. Now I am going to dive deeper into Generics and talk about a facet of Generics known as Constraints.
When working with Generics you will quickly find out that when you are building your classes, structures, interfaces, delegates or generic methods from scratch that the only members that can be assumed on your generic type are those of object (Equals, GetHashCode, ToString). If you try to use anything else the BC “Background Compiler” in VB will put the old squiggly line under your code fragment.
Public Class MyCompareClass(Of T1 As IComparable, T2 As IComparable)
Public Function CompareEntries(ByVal valueToCompareFrom As T1, ByVal valueToCompareTo As T2) As Boolean
‘can’t do below
If valueToCompareFrom.CompareTo(valueToCompareTo) < 0 Then
End If
End Function
End Class
If you wanted to make this work you could cast the parameter valueToCompareFrom to IComparable but that would cause an unwanted dynamic type check during run-time which would add overhead as well as introduce a possibility for a run-time error if the type doesn’t support IComparable. The solution to this particular type of problem is to use Constraints which will eliminate casting and allow for compile time type checking.
Public Class MyCompareClass(Of T1 As IComparable, T2 As IComparable)
Public Function CompareEntries(ByVal valueToCompareFrom As T1, ByVal valueToCompareTo As T2) As Boolean
‘now you can do this
If valueToCompareFrom.CompareTo(valueToCompareTo) < 0 Then
End If
End Function
End Class
‘This declaration works
Dim myTestCompare As New MyCompareClass(Of Integer, Integer)
‘This declaration does not work
Dim myTestCompare2 As New MyCompareClass(Of DataSet, DataSet)
What is going on in the syntax above is that T1 and T2 are limited to types that must implement the IComparable interface. If the values used during the class declaration do not implement this interface you will encounter a compiler error. Besides benefiting form compiler type checking you also get intelli-sense when working with the generic type inside of your code block which in this case means you get to see all of the interfaces exposed by IComparable. Since there are not a lot of examples on using constraints in VB.NET I am going to show you some different combinations you can use and explain what they do.
This example puts multiple constraints on T by comma delimiting the constraints inside of the { }. So here you would say that T has to implement both the IComparable and ICollection interafaces.
Public Class MyMulipleConstraint(Of T As {IComparable, ICollection})
End Class
This is the same thing with two parameters. This type of thing can go on for as many parameters as you want.
Public Class MyMultipleConstraint(Of T1 As {IComparable, ICollection}, T2 As {IComparable, ICollection})
End Class
This next example shows using a special case constraint. The New added here ensures that the type used for T has a public parameterless constructor and permits the generic class to create a new instance of T.
Public Class MyMulipleConstraint(Of T As {IComparable, New})
End Class
So as you can see Constraints can be very powerful and help eliminate dynamic type checking during run-time and run-time errors. They also provide you are the nicety of intelli-sense when working inside your class, structure, delegate or generic methods.