Generics. And why should make love to them. Constantly.
Let’s get back to basics. Usually any .NET-developer can get by using rudimentary development. Class this, method that. However, by actively pursuing to excel in your craftmanship you’ll discover just how awesome the .NET-framework is. Because that’s what development is: a craft. And as any professional, we should aim to excel and become better at what we do.
In .NET 1.1 we had some good tools.
In .NET 2.0 they became great.
Let me introduce you to Generics. After you’re done reading this entry, you’ll love them.
So what’s the problem?
All objects inherit from System.Object – this is the super class of .NET. The alpha class whereas your puny classes are all omegas. One class to rule them all! Ahem. Sorry, got carried away there.
Inheritance isn’t a bad thing. It’s what makes the framework extremely consistent.
There is a drawback to to this, however. Since all classes in the framework inherit from System.Object, any object can be cast to the System.Object class!
Why is this bad, you ask? The compiler could miss cast exceptions since casting to and from System.Object is always OK. It could also incur a performance penalty in your application when you cast between value and reference types.
You won’t catch compiler errors if you cast a an object of type String to System.Object, then try to cast that object to an Integer. Your application will instead throw an InvalidCastException.
string myString = "This is my text";
object myObject = myString;
myInteger = (int)myObject;
Boxing and unboxing
Value types are small pieces of data like integers, doubles, datetime, etc. They live in something called the stack. Reference types are different. They can grow quite large and exist on something called the heap. But in order to access these large pieces of data they have a reference which exists on the stack. By using that reference you can access the object.
This is all fine and dandy. But when you cast a value type to a reference type you perform something called boxing. This incurs a performance penalty. The same applies for casting a reference type to a value type.
It’s like putting your ferrari into a truck. It takes time. And the ferrari stops going fast until you take it out again.
Then why are Generics so awesome?
Let’s have a look at the syntax of Generics.
public class MyGenericClass<T, U>
where T : class
where U : struct
public T t;
public U u;
public MyGenericClass(T tInput, U uInput)
this.t = tInput;
this.u = uInput;
Notice that I have two types of parameters which the constructor can accept: T and U. This is nothing fancy – you can write a class that can accept two parameters of type System.Object. What is really cool here is that I’m using constraints. T must be a class (reference type) and U must be a struct (value type). If the submitted parameters are not of these types, the application will not turn into pie! I mean, compile.
This is how the declaration could look:
MyGenericClass<string, int> genericClass = new MyGenericClass<string, int>("Daniel Berg", 29);
Now the class contains my name and age. Sweet! Any logic I write now will understand that the first and second parameter of my generic class instance are of types string and int! Further I can create instances of MyGenericClass with perhaps System.Exception as the first parameter (the class one) and System.Double as the second (the struct one). This means that I have the flexibility of using any type of class and any type of struct, but I don’t need to cast back and forth between System.Object! And the compiler helps me out if I mess up.
Constraints on your generic class also supports interfaces, base classes, new() – which means that the type must have a parameterless constructor. By using constraints the code is guaranteed that the types will act like they’re intended to do.
So please, make love to your generics. Constantly.