31 March 2007

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.



blog comments powered by Disqus