Я использую следующие методы расширения для создания кэшированных версий делегатов:
public static Func<T, TResult> AsCached<T, TResult>(this Func<T, TResult> function)
{
var cachedResults = new Dictionary<T, TResult>();
return (argument) =>
{
TResult result;
lock (cachedResults)
{
if (!cachedResults.TryGetValue(argument, out result))
{
result = function(argument);
cachedResults.Add(argument, result);
}
}
return result;
};
}
public static Func<T1, T2, TResult> AsCached<T1, T2, TResult>(this Func<T1, T2, TResult> function)
{
var cachedResults = new Dictionary<Tuple<T1, T2>, TResult>();
return (value1, value2) =>
{
TResult result;
var paramsTuple = new Tuple<T1, T2>(value1, value2);
lock(cachedResults)
{
if (!cachedResults.TryGetValue(paramsTuple, out result))
{
result = function(value1, value2);
cachedResults.Add(paramsTuple, result);
}
}
return result;
};
}
public static Func<T1, T2, T3, TResult> AsCached<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> function)
{
var cachedResults = new Dictionary<Tuple<T1, T2, T3>, TResult>();
return (value1, value2, value3) =>
{
TResult result;
var paramsTuple = new Tuple<T1, T2, T3>(value1, value2, value3);
lock(cachedResults)
{
if (!cachedResults.TryGetValue(paramsTuple, out result))
{
result = function(value1, value2, value3);
cachedResults.Add(paramsTuple, result);
}
}
return result;
};
}
И так для N параметров ...
В случае, если из кода неясно, я создаю кортеж с аргументами и использую кортеж в качестве ключа к словарю, который содержит возвращаемые значения для каждого набора аргументов. Обратите внимание, что каждый раз, когда вы вызываете AsCached
, вы создаете отдельный кеш.
Вы можете использовать эти методы следующим образом:
private Func<int, SomeEntity> _getCached;
public SomeEntity Get(int id)
{
if (_getCached == null)
{
Func<int, SomeEntity> func = GetImpl;
_getCached = func.AsCached();
}
return _getCached(id);
}
private SomeEntity GetImpl(int id)
{
return base.GetItem<SomeEntity>
("select * from SomeEntities where id = @idParam",
new { idParam = id});
}