It's Generic, not Multipurpose

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.


Posted 09-09-2009 4:14 AM by sergiopereira
Filed under:

[Advertisement]

Comments

Hernan Garcia wrote re: It's Generic, not Multipurpose
on 09-09-2009 7:15 AM

I know that I have done some of that on my API's.

I really like that you summarize the smells of Generic over usage, very helpful.

I will keep them in mind.

Thanks for the post.

Harry M wrote re: It's Generic, not Multipurpose
on 09-09-2009 8:36 AM

It's generally done for readability, and the issues you outlined above aren't exactly showstoppers (more like dislikes), so I don't think you'll find anyone cutting them out of their APIs.

The gotcha I have encountered in the past is where someone has ONLY exposed generic api methods without ones that take (Type type). This actually made it very difficult to call the methods using reflection.

So I would say generic methods used to specify Type on APIs are bad IF you don't provide a non-generic version. Otherwise they are easy-to-grok sugar.

El Sid wrote re: It's Generic, not Multipurpose
on 09-09-2009 8:47 AM

Well I HAVE been show-stopped by a generic-only API and no option of a type instance parameter overload...

Most of the entity framework /EDM API is locked down to generic-only methods. This makes it very painful to work with multipurpose scenarios when you don't know the type at compile time.

sergiopereira wrote re: It's Generic, not Multipurpose
on 09-09-2009 8:47 AM

@Harry and @El Sid, you're absolutely right on the missing non-generic overloads, they really stink. Although I'd say that, many times, if you're able to provide a non-generic overload maybe the generic one falls in one of the categories I lined out.

DotNetKicks.com wrote It's Generic, not Multipurpose
on 09-09-2009 8:58 AM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Think before coding wrote re: It's Generic, not Multipurpose
on 09-09-2009 9:46 AM

I follow you on this.

There is still something that you can do with

component.GetInstancesOf<SomeType>() that you cannot do with the typeof variant : you can add Type parameter constraints like 'where T : ISomeInterface' It can sometime be usefull for type safety.

Scott Parker wrote re: It's Generic, not Multipurpose
on 09-09-2009 12:01 PM

Great insight Sergio.

I have mixed feelings regarding the "typeof" example though. Consider something like:

public IEnumerable<T> FindAllInstancesOf<T>();

Without generics, you are locked into something like this:

public IEnumerable FindAllInstancesOf(Type t)

Code written around the latter case ends up a little uglier. But if I'm going to have FindAllInstancesOf<T> in my codebase, then I'd prefer to keep everything consistent and provide DeleteAllInstancesOf<T> as well.

sergiopereira wrote re: It's Generic, not Multipurpose
on 09-09-2009 12:28 PM

@Scott, in that case there's a clear advantage of using the generic parameter as it affects the return value.

On the other hand, if you're going to have a bunch of generic methods like the ones you suggested, maybe you' could consider an entirely generic class, not individual generic methods.

Bill Barry wrote re: It's Generic, not Multipurpose
on 09-09-2009 1:28 PM

I think the first case is fine as long as both a generic and non-generic method are provided. In such cases you are really caring about the type. By using generics you can assure type safety at compile time and so can validate contracts. The non-generic counterparts are needed because .NET makes it overly difficult to call generic classes/methods when you don't know the types at compile time.

The other cases should not be using generics because they aren't dealing with the type in question but rather with data that can be found by the type (but likely other ways as well). This is nothing more than an application of the development axiom "Be Explicit" (a corollary of SRP).

DotNetShoutout wrote It's Generic, not Multipurpose - Sergio Pereira - Devlicio.us
on 09-09-2009 4:08 PM

Thank you for submitting this cool story - Trackback from DotNetShoutout

Sanjeev Agarwal wrote Daily tech links for .net and related technologies - September 9-12, 2009
on 09-10-2009 5:53 AM

HTML clipboard Daily tech links for .net and related technologies - September 9-12, 2009 Web Development

Brad wrote re: It's Generic, not Multipurpose
on 09-10-2009 5:43 PM

@El Sid

It can be done with a small amount of reflection.

typeof(SomeClass<>).MakeGenericType(type);

Line 42 of the first set segment of code:  www.bradleyboveinis.com/.../Integrating-CE-devices-into-your-NServiceBus-Architecture-(cont).aspx

sergiopereira wrote re: It's Generic, not Multipurpose
on 09-10-2009 9:08 PM

@Brad, I think what El Sid (and others) are referring to is the API you're trying to call not accepting a Type object (like the one you showed how to build) but only the generic version that expects a type identifier. E.g, you can call a method like DoStuff<Book>() but there isn't an overload like DoStuff( typeof(Book) ).

Brad wrote re: It's Generic, not Multipurpose
on 09-15-2009 3:28 AM

@sergio, I see, as a work around then you could do:

typeof(TClass).GetMethod("DoStuff", BindingFlags.Public | BindingFlags.Instance).MakeGenericMethod(typeof(Book)).Invoke(...);

Or, if the method signature is

DoStuff<T>(T obj) I don't think you need MakeGenericMethod and could just do

typeof(TClass).GetMethod("DoStuff", BindingFlags.Public | BindingFlags.Instance).Invoke(instance, new [] {genericParameter});

Brad wrote re: It's Generic, not Multipurpose
on 09-15-2009 3:47 AM

Correction, it seems you do require the MakeGenericMethod in some instances for the parameterized methods.

Bradley Boveinis wrote It's Generic, not Multipurpose.. but we can work around it!
on 09-15-2009 3:55 AM

It's Generic, not Multipurpose.. but we can work around it!

Xerxes wrote re: It's Generic, not Multipurpose
on 09-22-2009 4:05 AM

I agree with Harry. I think that the typeof() statements only add more noise.

The reason interface designers use generics like this is for the intended purpose of readability - and readability is king.

If we can piggyback the syntax behind generics (or lambdas, or anything else) to make code more readable/expressive (and not affect performance of the system) then that's better than the alternatives suggested here.

</2c>

sergiopereira wrote re: It's Generic, not Multipurpose
on 09-22-2009 9:07 AM

@Xerxes, if they generic versions were more readable, they would not need to have longer names, in my opinion.  

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)