EDIT: Contrary to what people understood from this post, it is NOT a “Do not use Generics” post – I think Generics are a GREAT addition to the language/framework and I use them all the time. It is against a very specific usage of Generics
Short Answer: Rarely.
Slightly longer answer: When you are aware of the alternatives.
Yet longer answer:
I have seen this a couple of times recently and thought I’d drop a line about it. As always, take it with a grain of salt, but here goes…
You find yourself wanting to write a method that returns a value to the caller. Said value can be of many types and you decide to create a template out of it – a recent example I saw (which is very typical to the misuse of this mechanism):
T GetRegValue<T>(string path); // Get the value of a registry path
This method calls into the Win32 wrappers and gets back an Object, the code then returns it to the caller in the following manner:
T result = (T)objectResult;
I have heard many reasons as to why people choose to use this mechanism – the two that come up most often:
1. It’s easier to understand/write/maintain or it is “cleaner”.
2. It’s more type-safe.
Lets go over those one by one…
It’s easier to understand/write/maintain or it is “cleaner”
The amount of code that needs typing in this case is exactly the same as if you had returned an object. Compare and contrast:
string result = (string)GetRegValue(“path”); // This is when using the “old and boring” way
string result = GetRegValue<string>(“path”); // This is when using the “new and shiny” way
It’s EXACTLY the same amount of code characters and I am pretty sure more people know how to read/parse casting operators (the (string) bit) than generic type parameters (the <string> bit). So it’s the same amount of code (not easier to write), it’s using a mechanism that less people know (not easier to understand). That leaves “easier to maintain” which I usually equate to “easier to understand”.
We are left with it’s “cleaner” but that’s a whole new blog entry – to me both mechanisms look alike from the cleanliness point of view.
It’s more type-safe
This is just not true. In the typical example, a cast is made to the requested type (T in this generic method) – casts in .NET are safe (that is to say, they will throw an exception of they do not work and if they do work they are guaranteed to work properly). The code doesn’t even eliminate the cast – it just moves it to the inside of the method.
Here’s my rationale behind why such a mechanism should not be used:
1. It’s harder to debug.
2. It doesn’t contribute anything.
It’s harder to debug
In the cases where the type of the retrieved cannot be cast, your exception will be thrown inside the inner method which may throw you off when you are trying to figure out what’s wrong. It’s not a big deal, especially if you own the library that is making the call, but it’s just a little harder to understand what went wrong.
It doesn’t contribute anything
Generics are a more advanced language construct that casting and as such is harder to understand. At the end of the day, you are getting the same result and so should be using the simplest mechanism possible.
So when should I use this?
There are cases where this can be useful – for example, when some property of the return type is used to make the method smarter. This is where you go onto the gray line. The above snippet, when presented to me, actually looked a little different:
T result = Convert.ChangeType(objectResult, typeof(T)); // Used to be T result = (T)objectResult;
This is a subtle difference, but an important one. Here we are asking the runtime to force one type unto another. To understand the difference, consider the following call (first one is the generic version and the second the non-generic):
double d = GetRegValue<double>(“path”);
double d = (double)GetRegValue(“path”);
In this case, the fancy version will actually work whereas the non-generic one will fail with an InvalidCastException. The reason behind this is simple – the API method used to fetch the value from the registry returns a boxed integer. A boxed integer can only be cast into two things – an Object or an Integer – it can’t be cast to any other type. For this to work, the caller would have had to do one of the following:
double d = (double)(int)GetRegValue(“path”); // Cast the object into an int and then the int into a double.
double d = Conver.ChangeType(GetRegValue(“path”), typeof(double)); // Coerce the value.
Both these choices ARE indeed somewhat less readable and perhaps less maintainable than the generic alternative – I personally could go both ways on this – not married to either of them. In the case of Registry access, the values are usually known beforehand and thus you probably know exactly what you are looking for so I would probably go with an object retval.