Question

I often find myself in a situation where I create a generic interface or class and then want to use different versions of this class or interface in a non generic way. For example I may have an interface like this:

interface ICanCreate<T>
{
    T NewObject();
}

Which allows a class to be a factory for that type. I then want to register these with a general factory class, so I try to write something like this:

public class Factory
{
    private Dictionary<Type, ICanCreate> mappings; // what do I put here????

    public void RegisterCreator<T>(ICanCreate<T> creator)
    {            
    }

    public T Create<T>()
    {            
    }
}

In the dictionary which type do I use for my value? I don't know if I am missing some sort of design principle and I am aware that this has a lot to do with co(ntra?)variance. Any help or ideas would be much appreciated.

Was it helpful?

Solution

You either have to just use object in your dictionary declaration (it's all private, and you can verify that you'll never put the wrong kind of thing in there) or declare a non-generic ICanCreate interface which ICanCreate<T> extends.

Basically you want a type relationship which can't be expressed in C# - and whenever that happens, you end up with a slightly unpleasant solution, but it looks like you can isolate the ugliness here (i.e. keep it within the single class).

OTHER TIPS

Interestingly, this is a problem that is solved in C# 4.0:

public interface ICanCreate<out T> // covariant
{
    T NewObject();
}

public class Factory
{
    private Dictionary<Type, ICanCreate<object>> mappings = new Dictionary<Type, ICanCreate<object>>();

    public void RegisterCreator<T>(ICanCreate<T> creator) where T:class
    {            
      mappings[typeof(T)] = creator;
    }

    public T Create<T>()
    {            
      ICanCreate<object> creator = mappings[typeof(T)];
      return (T) creator.NewObject(); // I do not think you can get rid of this cast
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top