Skip to content

Instantly share code, notes, and snippets.

@ValdemarOrn
Last active December 16, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ValdemarOrn/5445153 to your computer and use it in GitHub Desktop.
Save ValdemarOrn/5445153 to your computer and use it in GitHub Desktop.
/// <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