Simple generic cache I sometimes write decorators to cache results I get from a repository or I write other caches... I don't want to write another cache anymore. I'd like to have something that'll work for anything. I've build a test-cache that I could use to replace other caches. It provides just the most basic methods.

public class Cache
{
    private readonly IDictionary&lt;object, (object Value, DateTime CreatedOn, TimeSpan Timeout)&gt; _cache = new Dictionary&lt;object, (object Value, DateTime CreatedOn, TimeSpan Timeout)&gt;();

    public TResult Get&lt;TResult&gt;(object key, Func&lt;TResult&gt; get, TimeSpan timeout)
    {
        if (_cache.TryGetValue(key, out var t) &amp;&amp; !HasTimedout(t.CreatedOn, t.Timeout))
        {
            return (TResult)t.Value;
        }

        var value = get();
        _cache[key] = (value, DateTime.UtcNow, timeout);

        return value;

        bool HasTimedout(DateTime createdOn, TimeSpan currentTimeout) =&gt; DateTime.UtcNow - createdOn &gt; currentTimeout;
    }

    public async Task&lt;TResult&gt; GetAsync&lt;TResult&gt;(object key, Func&lt;TResult&gt; get, TimeSpan timeout)
    {
        return await Task.Run(() =&gt; Get&lt;TResult&gt;(key, get, timeout));
    }

    public void Clear() =&gt; _cache.Clear();

    public bool Remove(object key) =&gt; _cache.Remove(key);
}


On its own this is not very useful yet so I have a few extensions for it that help me to create keys which are tuples so that I can use their comparer that compares all properties and not the object reference:

public static class CacheExtensions
{
    public static TResult Get&lt;TResult&gt;(this Cache cache, Func&lt;TResult&gt; get, TimeSpan timeout)
    {
        return cache.Get&lt;TResult&gt;(null, get, timeout);
    }

    public static TResult Get&lt;T1, TResult&gt;(this Cache cache, T1 arg1, Func&lt;T1, TResult&gt; get, TimeSpan timeout)
    {
        return cache.Get&lt;TResult&gt;((arg1), () =&gt; get(arg1), timeout);
    }

    public static  TResult Get&lt;T1, T2, TResult&gt;(this Cache cache, T1 arg1, T2 arg2, Func&lt;T1, T2, TResult&gt; get, TimeSpan timeout)
    {
        return cache.Get&lt;TResult&gt;((arg1, arg2), () =&gt; get(arg1, arg2), timeout);
    }
}


Example usage:

var cache = new Cache();

cache.Get(2, 4, (x, y) =&gt; 3, TimeSpan.FromSeconds(4)).Dump("New");
cache.Get(2, 4, (x, y) =&gt; 4, TimeSpan.FromSeconds(4)).Dump("Cached");
Thread.Sleep(5000);
cache.Get(2, 4, (x, y) =&gt; 5, TimeSpan.FromSeconds(4)).Dump("Timedout");


Output:


New
3 

Cached
3 

Timedout
5



What do you think of it?
You say cache I say memory leak.

You haven't given much thought to your eviction policy yet and that's 95% of the work in writing a decent cache. Your expired entries are only removed when someone tries to access them.

Consider the case when the cache is used poorly - i.e. you end up caching a huge number of things that are only accessed once. Without a subsequent access after they are expired, they aren't removed and you have a whole bunch of wasted memory.

Your cache also lacks thread safety - that's also a really, really bad thing for a general cache that you intend on using everywhere. A cache doesn't need to be thread safe if you intend on using it in a single threaded application. However, I'd argue that isn't how most caches are used.

Why don't you use something like MemoryCache to do all the hard work?