Samstag, 1. September 2012

How To Use Generalisation With Generics

Hi, did you also have problems using generics with too specific parameters?

Example:
public abstract class Person 
{ 
  public void DoSomething(IEnumerable<Person> persons) { }
}

public class Teacher : Person { }
public class Student : Person { }

The question - why won't it work?

Exactly, if I want to call this method with IEnumerable<Student> I'd need to cast it. And not just normally cast, but with the extension method .Cast<Person>()
There are several more or less valid reasons for this need, which doesn't change the fact that it's annoying and doesn't serve the purpose of what I want to allow.

A very easy workaround which allows us to do everything we need:

public abstract class Person 
{ 
  public void DoSomething<T>(IEnumerable<T> persons) where T : Person { }
}

public class Teacher : Person { }
public class Student : Person { }

Ok, but what if I want my DoSomething of Teacher only be used with Teacher?

public abstract class Person<T> where T : Person 
{ 
  public void DoSomething(IEnumerable<T> persons) { }
}

public class Teacher : Person<Teacher> { }
public class Student : Person<Person> { }

And this was only the starter - what if you want to have a List of a List, but don't really care if it's an array, IEnumerable, Collection or an actual List?

public abstract class Person 
{ 
  // Limits you to IEnumerable<Person> as inner generic.
  // Example IEnumerable<Person>[] or List<IEnumerable<Person>> ...
  public void DoSomethingMore(IEnumerable<IEnumerable<Person>>) { }

  // No limitations
  public void DoSomethingBetter<T>(IEnumerable<T> persons) 
      where T : IEnumerable<Person> { }
}

public class Teacher : Person<Teacher> { }
public class Student : Person<Person> { }

As you can see you can call DoSomethingBetter any way of combining collections as you want. Even with Person[][] or List<HashSet<Person>>.

Happy Coding!

Peter