Generics: getting started with constraints

Now that we have basic knowledge about generics, we can proceed and talk about constraints. When we write “generic” code, the compiler must ensure that that code will work for any type which might be used in place of the generic type argument. For instance, take a look at the following code:

public class Test {
    void PrintInfo<T>(T genericObject) {
        Console.WriteLine(genericObject.ToString(  ));
    }
}

How can the compiler guarantee that the previous code will work for any type? (btw, lets ignore the fact that genericObject might be null). In the previous snippet, it’s really easy because we know that Object is the base class of all objects and ToString is one of the methods which is introduced by it. And what if we had the following code:

public class Test {
    void PrintInfo<T>(T first, T second)  {
        //compare both
        var comparison = first.CompareTo( second );
        if( comparison > 0 ) {
            Console.WriteLine("second smaller than first");
        }
        else if( comparison < 0 ){
            Console.WriteLine("first smaller than second");
        }
        else {
            Console.WriteLine("Equals");
        }
    }
}

The idea behind the previous snippet is simple: we’re expecting that T implements the IComparable<T> interface. If that happens, then we can call the CompareTo method over the first instance in order to compare them. As you’ve no doubt have noticed from the previous snippet, the compiler isn’t really happy with the CompareTo call. After all, implementing the IComparable<T> interface is really optional and the compiler can’t really generate IL code from the previous method that works for any type.

And that’s why we have constraints. With constraints, we can limit the number of types that can be specified for a generic type argument. By adding a constraint, we can make sure that the generic type argument will only be replaced by types which fall under certain conditions. So, lets fix the previous snippet so that it compiles:

public class Test {
    void PrintInfo<T>(T first, T second) where T:IComparable<T> {
        //compare both
        var comparison = first.CompareTo( second );
        if( comparison > 0 ) {
            Console.WriteLine("second smaller than first");
        }
        else if( comparison < 0 ){
            Console.WriteLine("first smaller than second");
        }
        else {
            Console.WriteLine("Equals");
        }
    }
}

A constraint is always specified through the where keyword and, in the previous example, it tells the compiler that T can only be replac
ed by a type which implements the IComparable<T> interface (trying to pass a type which doesn’t implement that interface results in a compiler error). With this small change, we can treat T like an object which implements IComparable<T>. Notice that constraints can be applied where you’re allowed to introduce generic types (ie, you can add constraints at class, interface, delegate or method level).

There are several types of constraints, which are grouped into several categories. In the next post, we’ll take a look primary constraints. Stay tuned for more.

Advertisements

~ by Luis Abreu on March 21, 2011.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: