Creating a dictionary with “enum-like” keys and strongly typed values

So here’s the idea I mentioned in my previous post.

A colleague of mine often uses (or at least he used to use) dictionaries to store settings, parameters, things like that. This isn’t altogether bad, but I personally am not a big fan of it. (I’ve talked to him about it, and his response has basically been: “Does it really matter?” To which I honestly have to respond, “Not really.”) The problem with this approach is probably best illustrated through code; so here’s an example of what I’m talking about:

// Note: hopefully it goes without saying that this is an oversimplified example,
// for illustration purposes only.
public enum CustomObjectSetting
{
    Name,
    Value
}

public class CustomObject
{
    Dictionary<CustomObjectSetting, object> _settings = new Dictionary<CustomObjectSetting, object>();

    public object GetSetting(CustomObjectSetting setting)
    {
        return _settings[setting];
    }

    public void ChangeSetting(CustomObjectSetting setting, object value)
    {
        _settings[setting] = value;
    }
}

Here’s why I basically hate this (though admittedly, the fact that I would hate it as opposed to just disliking it is really my own issue): while each setting might have a “correct” type, that type is enforced nowhere in the code itself (until the setting in question is used somewhere, where it is presumably downcast leading to a possible InvalidCastException). Any developers using this code need to just know what the type is for a particular setting.

This probably doesn’t seem like a big deal when you’re dealing with settings like, e.g., Name, which is presumably supposed to be a string. But it can happen (and does) that a setting is obviously numeric, but it’s unclear whether it’s supposed to be int, long, etc. Or that it seems like it should be numeric, but it’s actually an enum! (This actually happened once, where I assumed a setting with a name like “Size” (or something) was surely an int; but then it turned out it was an enum called something like QueueSize.)

These types of issues are resolved quickly through developer communication. But wouldn’t it be nice if there were some way to use a dictionary like this, with type safety? That is, if you could associate each enum value in some way with the type of the corresponding setting? After all, I get why my colleague likes to use dictionaries: they’re enumerable (without reflection), they’re easy to serialize, they’re fairly intuitive to use.1

Well, the reason I’m writing this post is to share with you that there is a way! In my opinion, it’s kind of a cool idea. Maybe you won’t agree with me; but anyway, here’s the idea. Instead of using a Dictionary<TKey, TValue> where TKey is an enum, define a new type that behaves like an enum, but with one crucial additional piece of information: the associated type.

Here’s a quick example of what I mean:

abstract class CustomField
{
	// Don't allow external code to inherit from this type.
	internal CustomField() { }
	
	// These readonly static fields will end up behaving essentially
	// like enum values.
	public static readonly CustomField<string> Name = new CustomField<string>();
	public static readonly CustomField<int> Value = new CustomField<int>();
}

sealed class CustomField<T> : CustomField
{
	// Don't allow external code to instantiate this type.
	internal CustomField() { }
}

class CustomDictionary
{
	// The keys of this internal dictionary are restricted to only certain values
	// (Name, Value), just like enum keys would be... but they will ALSO provide
	// type information.
	Dictionary<CustomField, object> _fields = new Dictionary<CustomField, object>();
	
	public void Set<T>(CustomField<T> key, T value)
	{
		// Since this is the only place where a value can be set...
		_fields[key] = value;
	}
	
	public T Get<T>(CustomField<T> key)
	{
		// ...this unboxing operation is safe (so long as the key exists)...
		return (T)_fields[key];
	}
	
	public bool TryGet<T>(CustomField<T> key, out T value)
	{
		object valueObject;
		if (_fields.TryGetValue(key, out valueObject))
		{
			// ...as is this.
			value = (T)valueObject;
			return true;
		}
		
		value = default(T);
		return false;
	}
}

In the above example, CustomField acts basically like an enum; but as you can see, its “values” (static fields) are actually instances of a derived generic class—CustomField<T>—so that each value is associated with a specific type.

This allows for the implementation of the CustomDictionary class, where there is a specific type associated with each key.

And voila, we have type safety with enum-like keys!

public class Program
{
	public static void Main()
	{
		var customDictionary = new CustomDictionary();
		customDictionary.Set(CustomField.Name, "Daniel");
		customDictionary.Set(CustomField.Value, 10);
		
		string name = customDictionary.Get(CustomField.Name);
		
		int value;
		customDictionary.TryGet(CustomField.Value, out value);
		
		Console.WriteLine("Name: {0}, Value: {1}", name, value);
	}
}

Output:


Name: Daniel, Value: 10

What do you think?

1: As I’ve already said, I don’t personally like this approach; but at least I understand it.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: