Last active
December 16, 2015 13:59
-
-
Save ValdemarOrn/5445153 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Cache/Memoizer class that stores output values based on input. | |
/// Can also expire elements and has a fixed lower and upper limit to the number of cached elements | |
/// </summary> | |
/// <typeparam name="TInput"></typeparam> | |
/// <typeparam name="TOutput"></typeparam> | |
public class Memoize<TInput, TOutput> | |
{ | |
private Dictionary<TInput, Tuple<TOutput, DateTime>> Values; | |
private int Min; | |
private int Max; | |
private int Expire; | |
/// <summary> | |
/// Creates a memoized container that caches a fixed maximum amount of objects | |
/// </summary> | |
/// <param name="expireSeconds">How many seconds after adding the element is it still valid. Set -1 for no expiration</param> | |
/// <param name="minSize">when the cache size gets to great, it gets reduced to this value</param> | |
/// <param name="maxSize">cache size gets reduced when it goes over this value</param> | |
public Memoize(int expireSeconds = -1, int minSize = 100, int maxSize = 200) | |
{ | |
Expire = expireSeconds; | |
Max = maxSize; | |
Min = minSize; | |
Values = new Dictionary<TInput, Tuple<TOutput, DateTime>>(); | |
} | |
public void Add(TInput key, TOutput value) | |
{ | |
lock (Values) | |
{ | |
var tuple = new Tuple<TOutput, DateTime>(value, DateTime.Now); | |
Values[key] = tuple; | |
if (Values.Count > Max) | |
ReduceSize(); | |
} | |
} | |
public Box<TOutput> Get(TInput key) | |
{ | |
lock (Values) | |
{ | |
var exists = Values.ContainsKey(key); | |
if (!exists) | |
return Box<TOutput>.Nothing; | |
var tuple = Values[key]; | |
if (Expire != -1 && (DateTime.Now - tuple.Item2).TotalSeconds > Expire) | |
return Box<TOutput>.Nothing; | |
return new Box<TOutput>(tuple.Item1); | |
} | |
} | |
public Box<TOutput> this[TInput key] | |
{ | |
get { return Get(key); } | |
set { Add(key, value); } | |
} | |
/// <summary> | |
/// Removes the oldest memos from the collection | |
/// </summary> | |
private void ReduceSize() | |
{ | |
lock (Values) | |
{ | |
int amountToRemove = Max - Min; | |
var keys = Values.OrderBy(x => x.Value.Item2).Select(x => x.Key).Take(amountToRemove).ToList(); | |
foreach (var key in keys) | |
Values.Remove(key); | |
} | |
} | |
/// <summary> | |
/// Very simple Maybe-ish class. Needed since Nullable can't handle class types. | |
/// Also, can contain null as a value | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
public class Box<T> | |
{ | |
public T Value { get; private set; } | |
public bool HasValue { get; private set; } | |
private Box() | |
{ | |
HasValue = false; | |
} | |
public Box(T value) | |
{ | |
Value = value; | |
HasValue = true; // even null can be a value | |
} | |
public static Box<T> Nothing { get { return new Box<T>(); } } | |
public override string ToString() | |
{ | |
return Value.ToString(); | |
} | |
public static implicit operator Box<T>(T value) | |
{ | |
return new Box<T>(value); | |
} | |
public static implicit operator T(Box<T> value) | |
{ | |
return value.HasValue ? value.Value : default(T); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment