Caffeine-Powered Life

Why C# Needs Duck Typing

I was working on a .NET project that lacked a few unit tests. So, being the Boy Scout developer I am, I decided I would add the ones that seemed obvious. Since this was a web project, that meant interacting with an active web session. The good news is that the web session has been wrapped with an adapter. The bad news is that the .NET framework is missing some obvious-to-me hooks.

HttpSessionStateBase.cs
1
2
3
4
5
6
7
8
public abstract class HttpSessionStateBase : ICollection, IEnumerable
{
    // snip

    public abstract object this[string key] { get; set;}

    // snip
}

Why isn’t the indexer encapsulated into it’s own interface? It’s just sitting out there, a property of the class definition. Basic key-value lookup seems like an appalling obvious role interface.

If it were its own interface, think about how easy it would be to jump back and forth from an in-memory Dictionary<string, object> to a live session. What’s your backing data? Who cares? This would make what I’m doing a heck of a lot easier.

IKeyValueStore.cs
1
2
3
4
public interface IKeyValueStore<TKey, TValue>
{
    TValue this[TKey key] { get; set;}
}

This interface should be retroactively added to all .NET objects that support indexing. This pattern pops up all over the place in .NET, so why not explicitly call it out when you see it? I’d even add it to System.String as an IKeyValueStore<int, char>. Of course, the setter would throw a NotImplementedException with a very nice explanation that .NET strings are immutable.

This is not the same as an IDictionary<KeyValuPair<TKey, TValue>, although dictionaries would also implement this interface. There’s a ton of extra stuff you need to make a dictionary — stuff I’m just not interested in writing. Nor do I want to have throw a ton of NotImplmentedException all over my codebase.

So why would duck typing matter? We wouldn’t care about the explict interface definition. Instead, we’d just call the indexer. If it works, then great! If not, then throw a NotImplementedException. In fact, this is exactly how the foreach keyword works.

In the end, I want to be able to jump back and forth between backing stores for the purposes of writing tests. This isn’t difficult, but it’s work I don’t feel like I should have to do because I want higher code quality.

A Gist of this code has been provided. For those of you keeping score at home, that’s 6 classes to something that should be really simple in 2013.

Comments