.NET, C#, Generics

Generics constraints


It has been a long time since the generics are around and most of developers are using them in their code. In this post, I would like to talk about how to use generics more effectively by using constraints.

 What are constraints?

Constraints are conditions applied on the parameter type that are checked at compile time.

Why constraints?

We use constraints to avoid runtime errors when we create generic types.

For instance, if we write a generic class that calculates an employee salary. We want this class to be generic because we may have different types of employee classes (this is for illustration only):

 

public class Employee
    {
        public string Name
        {
            get;
            set;
        }

        public doubleHourlyRate { get;
            set;
        }

        public double NumberOfWorkedHours
        {
            get;
            set;
        }
    }

    class PayCalculator<T>
    {
        public double CalculateEmployeeSalary(T employee)
        {
            //We need an employee from the T type
            Employeeemp = employee as Employee;
            returnemp.HourlyRate * emp.NumberOfWorkedHours; 
         } 
       }    

What’s wrong with this code?

At runtime, there is no guarantee that the type passed to PayCalculator is an Employee type. If it is not an Employee, the ’emp’ variable will have a null value which will make the last line of the CalculateEmployeeSalary method raise a null value exception. We can add a test to check whether or not the ’emp’ variable is null before we calculate the salary. This will fix a runtime error but still, a developer could instantiate our PayCalculator with a non valid type which will lead to an unexpected result.

To avoid this situation, we must add a constraint to our generic class. which will look like:

class PayCalculator<T> : where T:Employee
    {
        public double CalculateEmployeeSalary(T employee)
        {
            //We need an employee from the T type
            Employeeemp = employee as Employee;
            returnemp.HourlyRate * emp.NumberOfWorkedHours;
        }
    }

This means that whenever we instantiate a PayCalculator with a type, the compiler will check whether the passed type is an Employee type or not (actually, it can be a derivative of Employee). If it is not the case, it won’t compile at all.

Types of constraints:

There are 6 types of constraints:

  1. where T : <base class> this is the one we just have seen.
  2. where T: <interface> the type must implement a certain interface.
  3. where T: new() the type must have a parmeterless constructor
  4. where T: U in this case our generic has two parameters and the T one must inherit from type U.
  5. where T: struct T must be a value type
  6. where T:class T must be a reference type.

Note that constraints can be combined: you can have more than one constraints for a class. In this case, the ‘struct’ and ‘class’ constraints (respectively 5 and 6) must be the first constraints. The ‘new()’ constraint, on the other hand,  is always the last one to be specified.

You can find more details on MSDN: http://msdn.microsoft.com/en-us/library/d5x73970(VS.80).aspx

There is also good examples in the book “More effective C#”

 

Advertisements

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