Reputation: 696
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
?
Upvotes: 3
Views: 134
Reputation: 4186
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.
Upvotes: 1
Reputation: 26213
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;
}
Upvotes: 0