ln.collections/ln.collections/Cache.cs

225 lines
4.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace ln.collections
{
public class Cache<TKey, TValue>
{
public TimeSpan DefaultLifeTime { get; set; }
private readonly Dictionary<TKey, Entry> _cache = new Dictionary<TKey, Entry>();
public Cache(TimeSpan defaultLifeTime)
{
DefaultLifeTime = defaultLifeTime;
}
public Cache()
: this(TimeSpan.FromMinutes(10))
{
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
lock (this)
{
foreach (var cacheEntry in _cache)
{
if (cacheEntry.Value.IsValid)
array[arrayIndex++] = new KeyValuePair<TKey, TValue>(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<TKey, TValue> item) { lock (this) return TryGetValue(item.Key, out TValue value) && object.Equals(item.Value, value); }
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
public bool Remove(KeyValuePair<TKey, TValue> 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<TKey, TValue> factory)
{
lock (this)
{
if (!TryGetValue(key, out TValue value))
{
value = factory(key);
Add(key, value);
}
return value;
}
}
class Entry
{
private Cache<TKey, TValue> _cache;
public DateTime TimeOut { get; private set; }
public TValue Value { get; private set; }
public Entry(Cache<TKey,TValue> 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<TKey, TValue> : Cache<TKey, TValue>
{
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;
}
}
}