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<object, (object Value, DateTime CreatedOn, TimeSpan Timeout)> _cache = new Dictionary<object, (object Value, DateTime CreatedOn, TimeSpan Timeout)>();
public TResult Get<TResult>(object key, Func<TResult> get, TimeSpan timeout)
{
if (_cache.TryGetValue(key, out var t) && !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) => DateTime.UtcNow - createdOn > currentTimeout;
}
public async Task<TResult> GetAsync<TResult>(object key, Func<TResult> get, TimeSpan timeout)
{
return await Task.Run(() => Get<TResult>(key, get, timeout));
}
public void Clear() => _cache.Clear();
public bool Remove(object key) => _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<TResult>(this Cache cache, Func<TResult> get, TimeSpan timeout)
{
return cache.Get<TResult>(null, get, timeout);
}
public static TResult Get<T1, TResult>(this Cache cache, T1 arg1, Func<T1, TResult> get, TimeSpan timeout)
{
return cache.Get<TResult>((arg1), () => get(arg1), timeout);
}
public static TResult Get<T1, T2, TResult>(this Cache cache, T1 arg1, T2 arg2, Func<T1, T2, TResult> get, TimeSpan timeout)
{
return cache.Get<TResult>((arg1, arg2), () => get(arg1, arg2), timeout);
}
}
Example usage:
var cache = new Cache();
cache.Get(2, 4, (x, y) => 3, TimeSpan.FromSeconds(4)).Dump("New");
cache.Get(2, 4, (x, y) => 4, TimeSpan.FromSeconds(4)).Dump("Cached");
Thread.Sleep(5000);
cache.Get(2, 4, (x, y) => 5, TimeSpan.FromSeconds(4)).Dump("Timedout");
Output:
New 3 Cached 3 Timedout 5
What do you think of it?