Search code examples
c#genericsc#-2.0type-constraints

When should or shouldn't I be using generic type constraints?


I've got a base class:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

And two derived classes

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

Okay, now say I've got a list of items:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

and I want them all to call their DoSomething() method. I could expect to just iterate the list and call their DoSomething() method, so let's say I've got a method to do that called AllDoSomething() that just iterates over the list and does the job:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

What is the practical difference of the following method?

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

Both methods appear in real terms, although being syntactically different, to be doing the same thing.

Are they just different ways of doing the same thing? I understand generics and type constraints but can't see why I would use one way over the other in this instance.


Solution

  • This is because as of yet, C# does not support Covariance.

    More formally, in C# v2.0 if T is a subtype of U, then T[] is a subtype of U[], but G is not a subtype of G (where G is any generic type). In type-theory terminology, we describe this behavior by saying that C# array types are “covariant” and generic types are “invariant”.

    Reference: http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

    If you have the following method :

    public static void AllDoSomething(List<StuffBase> items)
    {
        items.ForEach(i => i.DoSomething());
    }
    
    var items = new List<Stuff2>();
    x.AllDoSomething(items); //Does not compile
    

    Where as if you use the generic type constraint, it will.

    For more information about Covariance and Contravariance], check out Eric Lippert's series of posts.


    Other posts worth reading :