Wednesday, June 25th, 2008

In praise of System.Object

By Joel Potischman

In this Quick Tip, I show you how to make a method that deals with System.Object parameters more type-safe by converting it to use generics. Last year I had written a wrapper class to talk to the ASP.NET caching system for me. It returned cached objects as System.Object for maximum flexibility. The client would cast to the desired type, but it always bugged me, because it wasn’t very type-safe. Consider the following chunk of code:

Cache.Set("WhenDidIDoThis", DateTime.Now);
...
int numberOfTicks = (int) Cache.Get("WhenDidIDoThis");

It will compile just fine, but it will blow up when I run it, because silly me, WhenDidIDoThis contains a DateTime, not an integer. It’s far safer to rewrite my code to use generics:

Cache.Set<datetime>("WhenDidIDoThis", DateTime.Now);
...
int numberOfTicks = Cache.Get<int>("WhenDidIDoThis");

Hmm. Actually, that same logic error still compiles just fine, but hey, at least my code now signals its intent more clearly. The bigger problem is now I’m getting a compiler error elsewhere. Let’s see why.

public static T Get(string key)
{
T cachedObject = (T) context.Cache.Get(key);
if(cachedObject != default(T))
logCacheHit(key);
return cachedObject;
}

Ah, that comparison with default(T) fails with the compiler error Operator ‘==’ cannot be applied to operands of type ‘T’ and ‘T’ because T can be literally any type, including one that doesn’t implement IComparable. Sure I could restrict my class to only accept IComparable types (public static T Get<T>(string key) where T : IComparable), but then my caching code is less universal. Or I could figure out a type-agnostic way of seeing if I got a cache hit without relying on IComparable. Yeah, that sounds like time well-spent.

So after a fair bit of work, my code is really no more type-safe than before, and I’ve either added complexity or limited its reusability, or more likely, both. And all because I wanted to replace my embarrassing System.Object parameters with sexier generic equivalents.

This class worked perfectly for every single use-case in every single project I’ve added it to over the past year (at least 10 so far). It doesn’t matter that it ignores every single advance in the .NET languages and framework since 1.0. It doesn’t matter that the other classes tease it for its positively Luddite reliance on System.Object. What does matter is realizing that a lot of the time in development, “good enough” isn’t just good enough, it’s actually pretty great. In hindsight, that’s a much more valuable Quick Tip.

2 Responses

  1. Avi Flax said:

    Sounds like a great example of Voltaire’s maxim The perfect is the enemy of the good. Jeff Atwood tackled this idea with his usual thoroughness back in January.

Leave a Comment