I sometimes need an object that caches some data but only for the specified amount of time. So I created a class that should handle this. The main goal was to make it generic unlike the MemoryCache
and be a single object cache and not a general all-pupose cache.
ObjectCache
public class ObjectCache<T>
{
private readonly object _valueExpiredMutex = new object();
private T _value;
public ObjectCache(TimeSpan expiration)
{
Expiration = expiration;
}
public EventHandler<ObjectCacheEventArgs<T>> ValueExpired { get; set; }
public TimeSpan Expiration { get; set; }
public DateTime? LastUpdate { get; internal set; }
public bool IsExpired
{
get { return !LastUpdate.HasValue || DateTime.Now - LastUpdate.Value > Expiration; }
}
public T GetValue()
{
if (!IsExpired)
{
return _value;
}
lock (_valueExpiredMutex)
{
_value = OnValueExpired();
LastUpdate = DateTime.Now;
}
return _value;
}
private T OnValueExpired()
{
if (ValueExpired == null)
{
throw new InvalidOperationException("ValueExpired event handler not set.");
}
var e = new ObjectCacheEventArgs<T>();
ValueExpired(this, e);
return e.Value;
}
}
LastUpdate
has aninternal
setter so that I can set it in tests- I tried to implement it to be thread-safe but I'm not sure if I did it correctly with only one mutex for getting a new value.
- Any kind of feedback is welcome ;-)
EventArgs
public class ObjectCacheEventArgs<T> : EventArgs
{
public T Value { get; set; }
}
Test
[TestMethod]
public void TestObjectCache()
{
var number = 1;
var objectCache = new ObjectCache<int>(
new TimeSpan(hours: 0, minutes: 21, seconds: 0));
objectCache.ValueExpired += (sender, e) =>
{
e.Value = number++;
};
Assert.IsTrue(objectCache.IsExpired);
Assert.AreEqual(1, objectCache.GetValue());
Assert.IsFalse(objectCache.IsExpired);
objectCache.LastUpdate = DateTime.Now.AddMinutes(-30);
Assert.IsTrue(objectCache.IsExpired);
Assert.AreEqual(2, objectCache.GetValue());
}
DateTime.Now
withDateTime.UtcNow
, which is not only faster but also not prone to unexpected behavior during time zone transitions. \$\endgroup\$ – Rick Davin Oct 27 '15 at 14:13