Before embarking on a wholesale conversion of WhaTBI? to take advantage of generics, I though it best to conduct a small experiment. The ultimate aim is to provide a datasource (one of the key WhaTBI? concepts) that supported "everything's an object" style access as is currently used, plus type-safe access via generics (I guess this would make it somewhat like "dual" interfaces in the COM world...).

So here goes... First we start off with a somewhat simplified version of the current datasource interface:

    interface ISimplifiedDataSource
    {
        void Add(object key, object value);
        object Get(object key);
    }

Then we add a simple implementation, and some test code (Whoops! Maybe that should have been the other way around...):

    class SimpleDataSource : ISimplifiedDataSource
    {
        private Hashtable data = new Hashtable();
        public object Get(object key)
        {
            return data[key];
        }
        public void Add(object key, object value)
        {
            data[key] = value;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Check(new SimpleDataSource());
        }
        static void Check(ISimplifiedDataSource ds)
        {
            ds.Add("Fred", "Flintstone");
            ds.Add("Barney", "Rubble");
            if (ds.Get("Fred").ToString() != "Flintstone")
                throw new ApplicationException("Test for Fred failed");
            if (ds.Get("Barney").ToString() != "Rubble")
                throw new ApplicationException("Test for Barney failed");
        }
    }

Now I wanted to check that I properly understood explicit interface implementation, so added another implementation of the interface that could also be used in a type-safe manner (in fact, it could only be used in the "object" way if being referenced as an ISimplifiedDataSource, as is the case in the testing code):

    class SaferDataSource<K, V> : ISimplifiedDataSource
    {
        private Dictionary<K, V> data = new Dictionary<K, V>();
        public V Get(K key)
        {
            return data[key];
        }
        public void Add(K key, V value)
        {
            data[key] = value;
        }
        object ISimplifiedDataSource.Get(object key)
        {
            if (key.GetType() != typeof(K))
                throw new ArgumentException("Key is of wrong type");
            return Get((K)key);
        }
        void ISimplifiedDataSource.Add(object key, object value)
        {
            if (key.GetType() != typeof(K))
                throw new ArgumentException("Key is of wrong type");
            if (value.GetType() != typeof(V))
                throw new ArgumentException("Value is of wrong type");
            Add((K)key, (V)value);
        }
    }

Then it was just a case of adding the appropriate line to test the new implementation to Main:

            Check(new SaferDataSource<string, string>());

All worked OK, but I'm still a little unhappy about type checking and having to cast the object parameters. It would be nicer to use as or something similar - any ideas?

The next stage was to see whether I could construct a generic interface (ISimplifiedDataSourceT) and have SaferDataSource implement that. The good news again was that the following was fine:

    interface ISimplifiedDataSourceT<K, V> : ISimplifiedDataSource
    {
        V Get(K key);
        void Add(K key, V value);
    }
}

So now all that remains is to extract out the explicit implementations into a helper class, and extend it a little for WhaTBI?. Whilst, of course, trying to make sure that I can build without the generics enhancements with V1.1 of the framework. Still, it was somewhat easier than I thought it might be...

UPDATE:

On looking back at the above code, I realise that I've made a rather embarassing mistake. My ISimplfiedDataSource interface is actually over-simplified, as both methods change parameters between the two schemes, making the explicit implementation unnecessary. This can be shown by removing the qualifiers from the methods in DataSourceHelper and re-testing (which I won't demonstrate here...).

Why I had thought I needed the technique was because some methods in the real IDataSource interface would only change their return values. For example:

ICollection GetAll() -> ICollection<V> GetAll()

When I was trying to do WhaTBI? as a "big design up front" project I often found a made such mistakes, mainly because my time working on it is so fragmented. This was one of the reasons I switched to TDD, so let's do the same here. First of we add the test for the new method to Check:

            if (ds.GetAll().Count != 2)
                throw new ApplicationException("Wrong count from GetAll()");

We're now at "red" as we don't even compile, so let's get to "green" by the shortest route possible. Firstly we add the method to ISimplifiedDataSource:

        ICollection GetAll();

Then we implement it SimpleDataSource:

        public ICollection GetAll()
        {
            return data.Values;
        }
    }

Finally, to implement it in SaferDataSource we use exactly the same code as above, but need to declare an abstract version in DataSourceHelper and thus mark the method in SaferDataSource as an override.

So now we got it to run successfully, time to refactor. We would obviously like a strongly-typed version of GetAll, so we add one to ISimplifiedDataSourceT<>:

        ICollection<V> GetAll();

However, this causes a problem in that it "hides" the version in the base interface. The obvious thing to do here is to break the inheritance relationship, which arguably should not have been there in the first place. Also, while we're at it, let's take a leaf out of the FCL book and drop the "T" from the end of the generic interface's name (We're not in C++, now, Dorothy!):

    interface ISimplifiedDataSource<K, V>
    {
        V Get(K key);
        void Add(K key, V value);
        ICollection<V> GetAll();
    }

Now to get things to work again! We'll make the GetAll method on SaferDataSource return a typesafe collection, and likewise the abstract method in DataSourceHelper. The helper class then needs to implement ISimplifiedDataSource independently of the generic version by just delegating to the asbtract version. This time explicit interface implementation is required (at last...):

    abstract class DataSourceHelper<K, V> : ISimplifiedDataSource<K, V>, ISimplifiedDataSource
    {
        public abstract V Get(K key);
        public abstract void Add(K key, V value);
        public abstract ICollection<V> GetAll();
        public void Add(object key, object value)
        {
            if (key.GetType() != typeof(K))
                throw new ArgumentException("Key is of wrong type");
            if (value.GetType() != typeof(V))
                throw new ArgumentException("Value is of wrong type");
            Add((K)key, (V)value);
        }
        public object Get(object key)
        {
            if (key.GetType() != typeof(K))
                throw new ArgumentException("Key is of wrong type");
            return Get((K)key);
        }
        ICollection ISimplifiedDataSource.GetAll()
        {
            return (ICollection)GetAll();
        }
    }
}

And there we have it - code that shows what I intended. The full source is here if anyone's interested...