Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Back to Basics: Generics

code{color:#833;background:#fcfcfc;}
h4{margin:30px 0px 0px 0px;font-color:#fff;font-weight:bold;border-bottom:1px dashed #ccc;font-variant: small-caps}

Introduction


There are language features that are nothing more than syntactical sugar. For example, C#’s coalesce operator (??) is a short-handed and specialized if-else. Object initializers make it easier to set properties on a newly created objects. Some features though go beyond mere convenience and add real value. I know it seems like we constantly have to learn new things, while at the same time actually produce code to pay our bills. It can be hard to pick and choose what to learn and what can wait. Let me be straight up though: if you haven’t mastered generics yet, you’re starting to fall dangerously behind.


I don’t say that to put you down. I say it because not only are they used all over the place (and it’s important that you understand the code that you use), but also because they’ve been around for a relatively long while and provide some serious benefits (generics were first introduced in .NET 3 years ago, but the concept dates back to the 70s).


I think the problem is threefold:


  • The syntax is odd,

  • The problem generics solve isn’t well understood, and

  • How they actually work isn’t clear

The Problem


The best way to learn how generics work is by looking at the problem they solve – which is rather straightforward. You use a generic whenever you have a piece of code which can be re-used by different types. The classic example is a data structure, like a list or a hashtable.


A list is a list regardless of the type of object being stored within it. Whether we’re adding a string or an int, or removing a User or a Textbox, the underlying code is the same. Without generics, we have two choices if we want to create a list that supports a wide range of types. The first is to use a list of System.Object (such as the built-in System.Collections.ArrayList). But this has two serious drawbacks. The first is one of type-safety:

var list = new ArrayList();
list.Add(“some name”);
int id = (int)list[0];

The above code will compile just fine, but it will cause a runtime error. The other is performance. The only way for .NET to cast a value-type (int, bool, long, float, …) to an object is to box and unbox the value (a technique used to transfer a value from the stack to the heap).


The other solution is to create an explicit list for each type that we want to support. This was so common that a CollectionBase class is included within the .NET framework for just this purpose. This approach performs well and is strongly-typed, but it requires a huge amount of repetitive code.


The Solution


We can use generics to solve the above-mentioned problems – the only real downside is that they can be intimidating. Let’s build a very simple generic list:

public class SimpleList<T>
{
private T[] _innerList = new T[10];
private int _count;

public void Add(T item)
{
if (_count+1 == _innerList.Length)
{
GrowOurList();
}
_innerList[_count++];
}
public T Get(int index)
{
return _innerList[index];
}
private void GrowOurList(){… do something …}
}


This is a simple generic class that can efficiently and safely support any type. Don’t worry about what GrowOurList does – a common growth algorithm is to simply create a new container that’s twice the size of the original and copy the old array into the newly created one. But what’s all this code really mean?


The magic happens around the class definition that defines the generic, <T>. T is an arbitrary name that I’ve given to our generic – some prefer to use more meaningful names, like <ListItemType>. T is a placeholder for any type. Within this class wherever you see T you should think of it as a yet-to-be defined type. So, when we create an instance of our list like:

var list = new SimpleList<int>();

The .NET runtime engine will replace every T with int, so our class actually becomes (just the relevant outline provided) :

public class SimpleList<int>
{
private int[] _innerList = new int[10];

public void Add(int item){…}
public int Get(int index){…}
}


If instead we create a list of strings, then T gets replaced with string. Notice that the containing array is actually turned into an array of integers – no boxing and unboxing. Also, notice that the parameter for Add is actually an int, as is the return type for Get – no unsafe casting.


Generic Repository


Hopefully the above example helps clarify what generics are and how they work. It isn’t a very realistic example though – you’ll likely never have to create such a list. But generics really can come in handy in a number of real situations. Say that you’re doing domain driven design and have a number of domain classes. You decide to have a repository for each domain (or maybe you decide to skip repositories and hit a DAL directly, or maybe you decide to put repository-like code directly in your domain, it doesn’t matter), you’ll likely end up with something like:

public class UserRepository
{
public User GetUser(int id) { … }]
public void Delete(User user) {….}
public void Save(User user) { … }
}

public class OrderRepository
{
public User GetOrder(int id) { … }]
public void Delete(Order order) {….}
public void Save(Order order) { … }
}

public class SupplierRepository
{
public User GetSupplier(int id) { … }]
public void Delete(Supplier supplier) {….}
public void Save(Supplier supplier) { … }
}


(of course you’ll have more methods, which won’t necessarily be shared by all repositories). This is an ideal candidate for a repository – similar code where the type is the main difference.

public class Repository<T>
{
public T Get(int id) { … }
public void Delete(T item) { … }
public void Save(T item) { … }
}

If you do need a specific non-generic method, you can easily do something like:

public  class UserRepository : Repository<User>
{
public User GetUser(string username, string password) { … }
}

Which’ll make the generic repository methods available to your UserRepository, in addition to the specific GetUser implementation.


I don’t want to get too far off topic, but you might be wondering how I’d actually implement our generic Get or Delete or Save method. My real answer is that you’d use NHibernate which itself relies on generics, so eventually Get<T>(int id) would call NHibernate’s own ISession.Get<T>(int id) method. Without getting into NHibernate though, a naïve (and foolish) implementation might look something like:

/* don’t actually try to build it this way! */
var sql = string.Format(“SELECT * FROM [{0}] where id = @id”, typeof(T).Name);

You can even take this a step further (and you probably should) and use a generic query object which would let you implement generic Find, FindAll, FindOne, Exists and Count methods:

var query = new ModelQuery<User>.Where(w => w.Criteria(“name”, Operation.Equals, “crush”).AtPage(2).LimitedTo(20);
var users = new Repository<User>().Find(query);

Multiple Generics


The syntax really gets confusing when you introduce multiple generics, such as the popular Dictionary<T, K>. There’s really nothing special here beyond what we’ve already covered. Instead of having 1 type placeholder, we have two. So given a method that looks like:

public void Add(T key, K value){… }

and code that instantiates our dictionary that looks like:

var dictionary = new Dictionary<int, User>();

the runtime engine actually turns our Add method into:

public void Add(int key, User value) { … }

It’s really that simple.


Constraints


All the examples we’ve looked at so far can support any type. Sometimes though you might want to limit the type that <T> can be replaced with. For example, in the case of our Repository, it doesn’t make sense to use an int. For such cases we can apply constraints to our generics:

public class Repository<T> where  T : class { … }

If we now try to create a Repository of type Int, we’ll get a compiler error. In addition to being able to constrain our generics to any interface or object, we can also constrain our generics to any reference type (as above) via the class keyword, or any value types via the struct keywords. The most interesting constraint is achieved via the new() keyword:

public class Something<T> where T : new() { … }

This means that the type must have a public-parameterless constructor. This is a special constraint that allows us to create an instance of the type T. The simplest example of where you’d want to use this is for a generic instance factory (which you probably wouldn’t need in day-to-day usage, but you might use tools, like mocking frameworks or DI frameworks that do):

public class GodOf<T> where T : new()
{
public T Create()
{
return new T();
}
}

Which can then be used via:

var instance = new GodOf<User>().Create();

Generic Methods


Generics don’t have to exist at the class level. They can actually exist at the method level. Our above class is probably a good candidate for this. We can change or GodOf class to:

public class God
{
public static T Create<T>() where T : new()
{
return new T();
}
}

With the following usage:

var instance = God.Create<User>();

Conclusion


Admittedly we looked at some pretty wild examples – a list that you’ll never create, an advanced generic repository with generic query objects, and an instance factory. Our goal though was to focus on understanding the problem generics solve, as well as zeroing in on that crazy syntax.


You do (or should) deal with generics on a daily basis – at the very least the generic collections built into .NET as of the 2.0 release. Once your understand of generics grow, not only can you use them in more advanced scenarios (like the built-in generic delegates), but also add them to your own code.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

13 Responses to Back to Basics: Generics

  1. leo.zhang says:

    good job,nice post!

  2. Chris Browne says:

    another awesome article karl, nice one.

    i take it on your repository implementations without generics, the GetXXX methods were not all supposed to return User objects right? 😉

  3. karl says:

    Good catch Shawn.

  4. Shawn says:

    I think line 12 of the second code block should be:
    _innerList[_count++] = item;

  5. Tim b says:

    I haven’t understood generics until just now! Thanks for the helpful post.

  6. Dom Finn says:

    Very helpful thanks. Perfect for me as I am just getting trying to get to grips with generics at the moment at work.

  7. karl says:

    ya, .NET 2.0, 3 years ago :)

  8. Xerxes says:

    “(generics were first introduced in .NET 3 years ago, but the concept dates back to the 70s)”

    I’d say you have a typo. Generics were introduced in .NET 2.0, not 3.0.

  9. Chris Missal says:

    Very well written Karl! This definitely deserves a place in your Foundations of Programming book. 😉

  10. Thanks for a great post!
    Even if you use generics on a daily basis it’s always good to go back to basics and contemplate what youre actually doing every once in a while.

  11. Elena says:

    Thank you for a very nice writeup. I like that you started out with “What problem does this solve?” That’s the key to understanding.

  12. Matt Corr says:

    Nice post. Thanks for the info. :)

  13. Lars Mæhlum says:

    Nice introduction. I will remember to bookmark this one so I have something to throw at the people who still refuse to learn generics.

    I finished my implementation of Repository for Linq2Sql yesterday.
    It makes bootstrapping new Linq2Sql projects that much easier.

    That people still rely on ArrayList etc. is just crazy.