У меня точно такая же проблема - в моем приложении многократно дорогие методы, и мне необходимо кэшировать эти результаты. Некоторое время назад я просто скопировал подобный код, но затем решил удалить эту логику из моего домена.
Вот как я это делал раньше:
static List<News> _topNews = null;
static DateTime _topNewsLastUpdateTime = DateTime.MinValue;
const int CacheTime = 5; // In minutes
public IList<News> GetTopNews()
{
if (_topNewsLastUpdateTime.AddMinutes(CacheTime) < DateTime.Now)
{
_topNews = GetList(TopNewsCount);
}
return _topNews;
}
И вот как я могу написать это сейчас:
public IList<News> GetTopNews()
{
return Cacher.GetFromCache(() => GetList(TopNewsCount));
}
Cacher - простой вспомогательный класс, вот он:
public static class Cacher
{
const int CacheTime = 5; // In minutes
static Dictionary<long, CacheItem> _cachedResults = new Dictionary<long, CacheItem>();
public static T GetFromCache<T>(Func<T> action)
{
long code = action.GetHashCode();
if (!_cachedResults.ContainsKey(code))
{
lock (_cachedResults)
{
if (!_cachedResults.ContainsKey(code))
{
_cachedResults.Add(code, new CacheItem { LastUpdateTime = DateTime.MinValue });
}
}
}
CacheItem item = _cachedResults[code];
if (item.LastUpdateTime.AddMinutes(CacheTime) >= DateTime.Now)
{
return (T)item.Result;
}
T result = action();
_cachedResults[code] = new CacheItem
{
LastUpdateTime = DateTime.Now,
Result = result
};
return result;
}
}
class CacheItem
{
public DateTime LastUpdateTime { get; set; }
public object Result { get; set; }
}
Несколько слов о Кахере. Вы можете заметить, что я не использую Monitor.Enter () (lock (...)) при вычислении результатов. Это связано с тем, что копирование указателя CacheItem (return (T) _cachedResults [code] .Result; строка) является потокобезопасной операцией - она выполняется только одним ударом. Также это нормально, если несколько указателей изменят этот указатель одновременно - все они будут действительны.