views:

643

answers:

6

Hello,

I'm looking for a way to have a generic local cache for any object. Here is the code :

    private static readonly Dictionary<Type,Dictionary<string,object>> _cache 
        = new Dictionary<Type, Dictionary<string, object>>();

    //The generic parameter allow null values to be cached
    private static void AddToCache<T>(string key, T value)
    {
        if(!_cache.ContainsKey(typeof(T)))
            _cache.Add(typeof(T),new Dictionary<string, object>());

        _cache[typeof (T)][key] = value;
    }

    private static T GetFromCache<T>(string key)
    {
        return (T)_cache[typeof (T)][key];
    }

1- Is there a way not to cast on the getfromcache method ?

2- Is there a way to ensure type safe in the second dictionnary, say all the object would have the same type. (This is provided by the addToCache method but I would prefer a type control in the design himself). For eg to have _cache of the following type

    Dictionnary<Type,Dictionnary<string,typeof(type)>>

Thx

+6  A: 

Try this:

static class Helper<T>
{
       internal static readonly Dictionary<string, T> cache = new Dictionary<string, T>();
}
private static void AddToCache<T>(string key, T value)
{
   Helper<T>.cache[key] = value;
}
private static T GetFromCache<T>(string key)
{
    return Helper<T>.cache[key];
}
Daniel
+1, way better because each type now has its own "namespace". On the downside, better not be using this from more than one thread.
Daniel Earwicker
Well, the question was about type-safety, not thread-safety.Public static methods should generally be thread-safe, so locks need to be added here.
Daniel
I really like this solution !
Toto
The solution is nice - BUT you get a static cache. For some applications this is useless. (It's like a Singleton, you can't instantiate more then one cache in a whole app-domain.)
Stefan Steinegger
Sorry stefan, I don't understand the issue ...
Toto
@renaud123 - in simple terms, you can only have one cache shared by all threads.
Daniel Earwicker
+2  A: 

Why not just put generic parameter to class declaration:

public class Cache<T>
{
    private Dictionary<string, T> _cache = new Dictionary<string, T>();
    ...

}

it can be static if you prefer

Dmitry Ornatsky
A: 

You won't be able to create a cache that is strongly typed if you don't know the required type at compile time. If you don't know the required type until run time then the answers to your questions are no and no, I'm afraid.

Filmund
+1  A: 

You don't gain much with this, unless that primitive types are not boxed.

private static readonly Dictionary<Type,Dictionary<string,object>> _cache 
    = new Dictionary<Type, IDictionary>();

//The generic parameter allow null values to be cached
private static void AddToCache<T>(string key, T value)
{
    // create a dictionary of the correct type
    if(!_cache.ContainsKey(typeof(T)))
        _cache.Add(typeof(T),new Dictionary<string, T>());

    _cache[typeof (T)][key] = value;
}

private static T GetFromCache<T>(string key)
{
    // casting the dictionary instead of the value
    Dictionary<string, T> typedDictionary = (Dictionary<string, T>)_cache[typeof (T)];
    return typedDictionary[key];
}

Of course it needs more not-found handling.

Stefan Steinegger
Not necessary, see Daniel's answer. Put a static field in a generic class and it automatically becomes an effective dictionary of types to values.
Daniel Earwicker
Thats true, but then you have a static dictionary, which is not always appropriate.
Stefan Steinegger
A: 

should probably toss in a few locks and maybe a few clones... but this should do.

class Program
{
    static void Main(string[] args)
    {
        CacheObject<int>.Instance["hello"] = 5;
        CacheObject<int>.Instance["hello2"] = 6;
        CacheObject<string>.Instance["hello2"] = "hi";
        Console.WriteLine(CacheObject<string>.Instance["hello2"]); //returns hi
    }
}

public class CacheObject<V> : CacheObject<string, V> { }
public class CacheObject<K,V>
{
    private static CacheObject<K, V> _instance = new CacheObject<K, V>();
    public static CacheObject<K, V> Instance { get { return _instance; } }

    private Dictionary<K, V> _store = new Dictionary<K, V>();
    public T this[K index]
    {
        get { return _store.ContainsKey(index) ? _store[index] : default(V); }
        set
        {
            if (_store.ContainsKey(index)) _store.Remove(index);
            if (value != null) _store.Add(index, value);
        }
    }
}
Matthew Whited
A: 

I have to throw this out there, this doesn't really seem like something you should be doing. Whatever you are doing most likely could be much better achieved by either using a Dependency Injection / Inversion of Control Library, a tool like Enterprise Library CacheManager or using a distributed memory caching program such as Memcache or Microsoft Velocity.

Chris Marisic