Introduction to C# Generics

What are generics? Are they for you? Should you use them in your applications? In this article, we'll answer these questions.
By Mihai Bodea Posted 15 September 2016

Introduction to C# Generics

The 'Generic' word is defined as referring to the members of a class as a whole, something general, with no label. Perhaps the best way to express what it actually means is by expressing what it is not.

It is not specific. It is not precise. It is just like a can that has no name on it. It might contain juice, beans or anything else, and it might as well have different labels for each thing it contains.

Generic Can

The first widely used implementation of generics in a programming language was in the early 1970s. The ML functional programming language was the first to allow writing functions that differ only in sets of types.

Generics and abstractions are two different concepts. Sometimes they are confused due to the generalisation they offer. Abstract code expresses an idea but not how to make it work, while generic indicates more a piece of code that works with more than one kind of object. Moreover, they serve different goals as abstractions are used for generalisation of common functionality also involving polymorphism while generics are used for similar implementations which solely differ in the type of object operating with.

What's the purpose of generics? Why should one use them?

The purpose of generalizing is to have a way of targeting multiple items.

In C# you can refer to multiple object types by using the convention 'T' letter, or any other letter/combination of letters/digits.

Coming back to abstractions vs. generics, consider the following:

We might need an abstraction for a service that does currency exchange between different currencies.

public interface ICurrencyService
{
    double Exchange(Currency from, Currency to, double amount);
}

On another hand, for a cache in which we might want to store the currency rates and some other information (type agnostic), we might want to use generics.

public static class CacheService<T>
{
    private static List<T> CachedItems = new List<T>();

    public static void AddItemToCache(T item)
    {
        CachedItems.Add(item);
    }

    public static List<T> GetCachedItems()
    {
        return CachedItems;
    }

    public static void ClearCache()
    {
        CachedItems = new List<T>();
    }
}

Using generics helps reducing code duplication by reusing the same implementation for multiple types, but more importantly ensures the code is less error prone and more flexible. Don't forget the code you don't need to write means code that you don't need to test, besides having the mistake area diminished by having a single point of modification.

C# ensures type-safety at compile time together with no performance penalty by avoiding boxing and unboxing (casting your type to and from 'System.Object').

A simple comparison to demonstrate the performance difference http://rextester.com/YZPT39367

How do they work?

Generics are integrated directly into the CLR. That's the Common Language Runtime, responsible for managing the execution of the .NET programs.

The definition of the generic type is maintained in memory at runtime. When a new concrete type is required, the runtime uses reification to create the new type. Basically, the runtime environment combines the generic type definition and the type arguments. This means, for each combination of type arguments we get a new type.

As you see here, these types are not equal... http://rextester.com/IABCK53661

Due to all this, at compilation type you get errors when something is not right.

Main features

Just think that you can define an algorithm without knowing what it's operating on. How mind blowing is that?

The greatest feature is LINQ (Language INtegrated Query) which would have simply not been possible without generics.

By default, generic types are matching the common characteristic in C#, by deriving from the System.Object class.

Another thing is the full support from the compiler. It does not allow you to do anything 'illegal' as your code won't compile, just like when you don't use generics. You can operate on groups of objects of the same thing.

Generics can be used for parameters / properties / variables. Moreover, you can define generic constructors, methods, classes, interfaces, delegates and even events.

Constraints

A generic type can have different constraint types which can be listed using the 'where' keyword.

One can specify if the passed type must have an empty constructor, if it must be a class/struct, if it should derive from a class, or if it should implement some interface(s).

public void Process<T>(T dataItem) where T : new() { }
public void Process<T>(T dataItem) where T : struct { }
public void Process<T,U>(T dataItem, U enumerator) where T : IComparable
                                                   where U : IEnumerator { }

Restrictions for constraints

There are though, some restrictions that apply in using the constraints. As mentioned earlier, there is a constraint which enforces only types that have a constructor to apply to that certain generic type.

Unfortunately the constraint does not allow specifying a type should have a constructor with parameters.

Another restriction, given by the .Net framework this time is regarding multiple inheritance as this is not possible at class level.

Value types cannot be a constraint, as they are sealed and you can't inherit them either.

Also, class/struct/constructor cannot be combined in a constraint and are designed to be used separately.

Usage restrictions

There are a few usage restrictions which are worth mentioning.

You cannot define a generic attribute (a class that inherits from System.Attribute), and Microsoft explains why they added this limitation:

'An attribute decorates a class at compile-time, but a generic class does not receive its final type information until runtime. Since the attribute can affect compilation, it has to be 'complete' at compile time.'

Methods or classes that have parameters with generic types must also have the generic type declared.

There are also limitations in using generics on platforms like iOS using Xamarin due to statically compilation ahead of time instead of being compiled on demand by the JIT compiler.

Ways to use generics

Because the only common characteristic of the generic types is the fact they inherit the Object base class, the only methods available are the inherited ones.

Object Intellisense

This means you won't get to do much with generics, right?

Well, if you don't really want to do much with the object that comes as a parameter in your method, you don*t care about its internal state and the only thing you want to do is store it in a collection, probably you're ok.

But, there are several ways to do more than just manipulate the object as a whole, take it from here and put it there.

If you do care about its internals you can have an abstraction for the entire suite of object you are working with, and by doing a cast to that abstraction you can access the properties that you want. Smart!

What about cases in which you cannot use abstractions because the objects are not at your disposal? Perhaps the types you're using come from an external library… If the situation is that bad there is a 'dark magic' that can help. Reflection allows you to obtain type information and data at runtime.

Just beware that reflection brings a performance penalty if abused, so this usage mode is the least preferred, although using generics brings you a lot of times on the border.

How do you know you should use generics?

When you do the same implementation over and over again, and there are tiny variations in data or behavior, the only different thing is mainly the type of the object you're doing the processing against you need to start thinking about generics.

This situation is a code smell named Combinatorial Explosion.

Feature Image: https://vvvv.org/blog/generic-nodes-project

Mihai Bodea
Mihai Bodea
Middle Software Developer