Wrox Home  
Search
Professional .NET 2.0 Generics
by Tod Golding
October 2005, Paperback


Excerpt from Professional .NET 2.0 Generics

Using Generic Methods

By Tod Golding

So much attention gets paid to the new generic classes that have been introduced with .NET generics, I often find developers overlooking what can be achieved through the use of generic methods. In fact, in many cases, you may discover one of the first opportunities to leverage generics is by introducing some generic methods into your non-generic types. This brings the flavor and power of generics to a class without requiring the class itself to be parameterized.

The Basics

To illustrate the fundamental value of generic methods, let's start with the simplest of examples. Suppose you have a Max() function that accepts two double values, compares them, and returns the greater of the two values. This function might appear as follows:

[VB code]
Public Function Max(ByVal val1 As Double, ByVal val2 As Double) 
As Double
    Return IIf(val2 < val1, val1, val2)
End Function 
[C# code]
public double Max(double val1, double val2) {
    return (val2 < val1) ? val1 : val2;
}

This method is handy for number-crunching applications. However, once you decide you want to apply this same Max() function to additional data types, you have a problem. This method can only be applied to double data types. You only have a few real, type-safe options that you can use to resolve this problem. One approach would be to create specific versions of this method to support each data type. However, doing that would force you to bloat your namespace with MaxString, MaxInt, and MaxLong methods. Not good. To get around the bloat issue, you might consider going back to using an object-based interface and tossing all type safety to the wind. Your last option here would be to provide several overloaded versions of Max() that accepted different types. That might represent some measure of improvement, but it's still not ideal.

This discussion of taking on bloat or compromising type safety is probably starting to sound like a broken record at this point. You see the same patterns over and over again in your code. You start out with a nice, general-purpose class or method only to find that, as you attempt to broaden its applicability, you discover that the tools offer you few good options to extrapolate that generality to additional data types. That's right in the sweet spot of generics.

So, let's look at how generics can be applied to the Max() method. The following code represents the generic version of the Max() method:

[VB code]
Public Function Max(Of T As IComparable)
(ByVal val1 As T, ByVal val2 As T) As T
    Dim retVal As T = val2
    If (val2.CompareTo(val1) < 0) Then
        retVal = val1
    End If
    Return retVal
End Function
[C# code]
public T Max<T>(T val1, T val2) where T : IComparable  {
    T retVal = val2;
    if (val2.CompareTo(val1) < 0)
        retVal = val1;
    return retVal;
}

The syntax and concepts here are right in line with what you've already seen with generic classes. The Max() method becomes a parameterized type, accepting one or more type parameters as part of its signature. Once you've outfitted your method with a type parameter, you can then proceed to reference that type parameter throughout the scope of the function. Method parameters, return types, and types appearing in the body of your methods may all reference the type parameters that are supplied to your generic method.

For this example to work properly, I was required to apply a constraint to my type parameter, indicating that each T must implement IComparable. Constraints are addressed in detail in the book Professional .NET 2.0 Generics Chapter 7, "Generic Constraints."

All that remains at this stage is to start making some calls to this new, generic Max() method. Let's take a quick look at how clients would invoke the Max() method with a few different type arguments:

[VB code]
Dim doubleMax As Double = Max(Of Double)(3939.99, 39999.99)
Dim intMax As Int32 = Max(Of Double)(339, 23)
Dim stringMax As String = Max(Of String)("AAAA", "BBBBBB")
[C# code]
double doubleMax = Max<double>(3939.99, 39999.99);
int intMax = Max<int>(339, 23);
string stringMax = Max<string>("AAAA", "BBBBBB");

Calling a generic method, as you can see, is not all that different than calling a non-generic method. The only new wrinkle here is the introduction of a type parameter immediately following the name of the method.