The technique we used to build PolyDictionary can be used in other
places. For example, you can adapt a non-type-checked store to a
type-checked store by supplying your own Get() and Set()
methods. One place this might be useful is when you encounter something like
CodeDom's UserData. It is intended to allow users or producers
of a CodeDom to annotate a
CodeDom with their own information. Because it uses IDictionary,
using UserData is not type checked at compile
time. You can introduce type checking by using your own methods to access the
dictionary like,
public static T Get<T>(IDictionary dictionary, Key<T> key) {
return (T)dictionary[key];
}
public static void Set<T>(IDictionary dictionary, Key<T> key, T value) {
dictionary[key] = value;
}
which uses the Key<T> class from last time. This allows
the compiler to ensure that the type of the value you get out is the same as you
put in and vise versa.
With C# 3.0, however, you can get even more creative. You can create extension
methods for IDictionary that makes it appear that all IDictionarys have a type
checked Get() and Set() like,
static class IDictionaryExtensions {
public static T Get<T>(this IDictionary dictionary, Key<T> key) {
return (T)dictionary[key];
}
public static void Set<T>(this IDictionary dictionary, Key<T> key, T value) {
dictionary[key] = value;
}
}
This allows you to write code like,
Key<string> k1 = new Key<string>();
Key<int> k2 = new Key<int>();
Key<double> k3 = new Key<double>();
IDictionary dictionary = new Hashtable();
dictionary.Set(k1, "one");
dictionary.Set(k2, 2);
dictionary.Set(k3, 3.33);
string v1 = dictionary.Get(k1);
int v2 = dictionary.Get(k2);
double v3 = dictionary.Get(k3);
Console.WriteLine("v1 = {0}", v1);
Console.WriteLine("v2 = {0}", v2);
Console.WriteLine("v3 = {0}", v3);
which allows us to use an IDictionary almost identically to a
PolyDictionary.
This works great for Get() and Set() but this
doesn't work for Add(), unfortunately.
The compiler accepts it but it doesn't do what we want. If we use a value
of an incorrect type for a particular key we want the compiler to generate and
error. In other words, if we change the Set() call for k1 above to,
dictionary.Add(k1, 1);
we want the compiler to generate an error. This doesn't happen because of method overloading. The object version of
Add,
Add(object value) is called if the type is incorrect; this is not what we want.
We could add an AddSafe() method to the extension class but then we
would have to remember to call it instead of Add(), which defeats
the purpose in my opinion.