using System; using System.Collections.Generic; using System.Linq; namespace ln.collections { public class Cache { public TimeSpan DefaultLifeTime { get; set; } private readonly Dictionary _cache = new Dictionary(); public Cache(TimeSpan defaultLifeTime) { DefaultLifeTime = defaultLifeTime; } public Cache() : this(TimeSpan.FromMinutes(10)) { } public void CopyTo(KeyValuePair[] array, int arrayIndex) { lock (this) { foreach (var cacheEntry in _cache) { if (cacheEntry.Value.IsValid) array[arrayIndex++] = new KeyValuePair(cacheEntry.Key, cacheEntry.Value.Value); } } } public int Count { get { lock (this) { return _cache.Count; } } } public bool IsReadOnly => false; public void Clear() { lock (this) _cache.Clear(); } public void Cleanup() { lock (this) { foreach (var key in _cache.Keys.ToArray()) { if (!_cache[key].IsValid) _cache.Remove(key); } } } public bool Contains(KeyValuePair item) { lock (this) return TryGetValue(item.Key, out TValue value) && object.Equals(item.Value, value); } public void Add(KeyValuePair item) => Add(item.Key, item.Value); public bool Remove(KeyValuePair item) { lock (this) { if (_cache.TryGetValue(item.Key, out Entry entry) && object.Equals(item.Value, entry.Value)) { _cache.Remove(item.Key); return entry.IsValid; } return false; } } public void Add(TKey key, TValue value) { lock (this) { _cache.Add(key, new Entry(this, value)); } } public bool ContainsKey(TKey key) { lock (this) return _cache.ContainsKey(key) && _cache[key].IsValid; } public bool Remove(TKey key) { lock (this) return _cache.Remove(key); } public bool TryGetValue(TKey key, out TValue value) { lock (this) { if (_cache.TryGetValue(key, out Entry entry)) { if (entry.IsValid) { value = entry.Value; return true; } else { _cache.Remove(key); } } } value = default(TValue); return false; } public TValue this[TKey key] { get { lock (this) { if (TryGetValue(key, out TValue value)) return value; } throw new KeyNotFoundException(); } set { lock (this) { if (_cache.TryGetValue(key, out Entry entry)) entry.Reset(value); else _cache.Add(key, new Entry(this, value)); } } } public bool ReValidate(TKey key) { lock (this) { if (_cache.TryGetValue(key, out Entry entry)) { if (entry.IsValid) { entry.UpdateTimestamp(); return true; } else { _cache.Remove(key); } } return false; } } public TValue GetOrCreate(TKey key, Func factory) { lock (this) { if (!TryGetValue(key, out TValue value)) { value = factory(key); Add(key, value); } return value; } } class Entry { private Cache _cache; public DateTime TimeOut { get; private set; } public TValue Value { get; private set; } public Entry(Cache cache, TValue value) { _cache = cache; Value = value; UpdateTimestamp(); } public void UpdateTimestamp() => UpdateTimestamp(_cache.DefaultLifeTime); public void UpdateTimestamp(TimeSpan lifeTime) => TimeOut = DateTime.Now + lifeTime; public bool IsValid => TimeOut > DateTime.Now; public void Reset(TValue newValue) { Value = newValue; UpdateTimestamp(); } } } public abstract class KeyedCache : Cache { protected KeyedCache() { } protected KeyedCache(TimeSpan defaultTimeOut) :base(defaultTimeOut) { } protected abstract TKey GetKey(TValue value); public void Add(TValue value) => Add(GetKey(value), value); public bool RemoveValue(TValue value) { TKey key = GetKey(value); lock (this) { if (TryGetValue(key, out TValue cachedValue) && object.Equals(cachedValue, value)) { return Remove(key); } } return false; } } }