3

I just encountered an interesting Problem concerning Generics and covariance and, long story short, spent 30 minutes trying to declare a Type until i gave up.

For clarity: I already have a workaround for this, and do not need help with my project currently. I just asked this question because i am a fan of perversely complicated generic types. If you are too, enjoy.

I was trying to define this method to fetch a global IDictionary, that manages all objects of type T by their IDs. (IDs are only unique among objects of the same type).

IDictionary<int, T> getCache<T>() where T : BaseClass { }

To avoid checking T for every derivative of BaseClass (of which there are many) i wanted to define a global Dictionary of Dictionaries, to look up the correct List.

I tried something like this:

Dictionary<Type, IDictionary<int, Baseclass>> allCaches;

Expierienced users of Generics might see the problem with this implementation: the IDictionary<TKey, TValue> interface is not covariant.

(Not covariant means, IDictionary<int, DerivedClass> does NOT inherit from IDictionary<int, BaseClass>. As a result, objects of the former type cannot be placed in my Dictionary allCaches)

I ended up, just using an IDictionary<int, BaseClass> for all my caches, and manually cast back the stored elements when i read them.

I wonder, can anyone think of an implementation for my method getCache<T>() that uses minimal casting and does not manually branch for all types derived from BaseClass?

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445

2 Answers2

1

I would use a generic static type as substitute for aDictionary<Type,T> provided I can live with those caches being singletons.

public static class Caches
{
    public static class For<T>
    {
        public static IDictionary<int,T> Cache{get;}=new Dictionary<int,T>(); 
    }

    public static Set<T>(int key,T value)=> For<T>.Cache[k]=v;
}

// and to use the dictionary
public void DoStuff(int i, string value){
    Caches.For<string>.Cache[i]=value;
    // or if you define generic methods in Caches like Set 
    Caches.Set(i,value);
}

Remember that Class<int> and Class<float> are two distinct types who share the same generic type definition Class<T>.

There is no runtime type such as Class<T> as it is an Open Generic Type whereas Class<int> and Class<float> are Closed Generic Types as explained in Tony The Pony's SO answer on Open and Closed Generic Types

This is why static members of Class<float> and Class<int> are distinct, those are two distinct classes that redefine all their members each time the generic parameter changes.

Community
  • 1
  • 1
Florian Doyon
  • 4,146
  • 1
  • 27
  • 37
  • I can't use singletons for my particular problem, but might use it for other parts of my project. Definitely didn't think of that, thanks for the sugggestion. – TheHowlingHoaschd Jul 26 '16 at 10:07
0

I'd be inclined to just ignore the type for the value in your 'dictionary of caches' and cast it on return:

private readonly Dictionary<Type, object> allCaches = new Dictionary<Type, object>();

public IDictionary<int, T> GetCache<T>() where T : BaseClass
{
    object cache;

    if (!allCaches.TryGetValue(typeof(T), out cache))
    {
        cache = new Dictionary<int, T>();
        allCaches.Add(typeof(T), cache);
    }

    return (IDictionary<int, T>) cache;
}
Charles Mager
  • 25,735
  • 2
  • 35
  • 45