Sergio and the sigil

It's Generic, not Multipurpose

Posted by Sergio on 2009-09-09

There's something that has been bothering me with many .Net APIs that are Generics-based (or that at least try to be.) I might be totally off or just nitpicking but I could not find any reference agreeing or disagreeing with the opinion I'm about to give.

Generic parameters are not data

Well, that pretty much summarizes what I think and I could stop right here. On the other hand, doing that would make for a pretty lousy blog post. Let's elaborate on that statement a little more.

The way I like to see the generic parameters in a function declaration (those that go inside the < and >) is akin to modifiers. They modify the function's data parameters (the normal parameters) and/or return value's data type.

Here are some examples, loosely based on some APIs you may be familiar with but changed to avoid singling out anyone. Some of the examples are really popular.

The typeof hater

Take a look at this generic function call.

cachedData.DeleteAllInstances<Book>();
// or even
cachedData.DeleteAllInstancesOf<Book>();

I could almost bet that the generic methods above only use the Book generic parameter in a statement like typeof(T). So what they really need is the Type object not the type identifier, and that's exactly what the following non-generic version asks for.

cachedData.DeleteAllInstances( typeof(Book) );

The Stringophobic

It always feels weird when I find something like this:

var list = container.FindInterfacesInNamespace<IOneOfThose>();
// or even
var list = container.FindInterfacesInNamespaceOfType<IOneOfThose>();

That function call feels like someone is desperately trying to avoid passing the namespace string as a data parameter, possibly thinking that could hurt automated refactoring. I'd just go with this:

string interfacesNamespace = typeof(IOneOfThose).Namespace;
var list = container.FindInterfacesInNamespace(interfacesNamespace);

The thumb buster

Sometimes I think API designers are just trying too hard to use generics. I mean, if there's a way to get the data they need by way of a type identifier, no matter how unnatural that be, a generic function will be born. Paraphrasing what someone already said: When a new language feature is your hammer, everything starts to look like your thumb.

var list = container.
    FindAllImplementationsOfTypeAinTheAssemblyOfTypeB<IDoStuff, ConcreteDoer>();

You see, the first generic parameter in this example is fine (assuming the function returns something like IEnumerable<IDoStuff> ). The problem is the second one. If all you really need is an Assembly object, just ask for it, I'd say.

var asm = typeof(ConcreteDoer).Assembly;
var list = container.FindAllImplementationsInAssembly<IDoStuff>(asm);

How to avoid that

Here are some symptoms of function declarations that may be suffering from generic parameter over-excitement.

  • There's no way to skip explicit generic parameter passing, i.e. it's impossible for the compiler to infer the generic parameter from the data parameters.
  • The only usages of the generic parameter in the function implementation involve the typeof operator.
  • The generic parameter in question doesn't have any constraints (the where keyword in C#) in its declaration. Or maybe it does have but it's not being leveraged at all in the implementation and can be safely removed (maybe there's a Resharper code inspection suggestion for that.)
  • Because the generic parameters are being used in an unnatural way, the function name and/or the generic parameter name become much longer than the usual, in an attempt to explain the unexcusable.

As I said, I may just be too sensitive or completely missing the point, neither of which would be their first time. Worst case someone chimes in with clever remarks and I learn something new, which again would not be the first time.