Just to avoid reinventing the wheel
I'm wondering whether a standard C# implementation already exists to cache the results from a long-running, resource-intensive method.
To my mind, the Lazy<T>
would be appropriate, but unfortunately it seems to lack the input parameter to index the result.
I hope the following helps to clarify: this is my custom solution.
public class Cached<FromT,ToT>
{
private Func<FromT,ToT> _my_func;
private Dictionary<FromT,ToT> funcDict;
public Cached(Func<FromT,ToT> coreFunc, IEqualityComparer<FromT> comparer = null)
{
_my_func = coreFunc;
if (comparer != null) {
funcDict = new Dictionary<FromT,ToT>(comparer);
} else {
funcDict = new Dictionary<FromT,ToT>();
}
}
public ToT Get(FromT fromKey) {
if (!funcDict.ContainsKey(fromKey)) {
funcDict.Add(fromKey, _my_func(fromKey) );
}
return funcDict[fromKey];
}
}
Find below my unit-testing code.
string DBSimulation(int example, bool quick = false) {
if (!quick) Thread.Sleep(15000);
return example.ToString();
}
[Test]
public void Test03Cached() {
var testCache = new Functional.Cached<int,string>(x => DBSimulation(x));
DateTime checkNow = DateTime.Now;
string logResult = "";
for (int i = 0; i < 24; i++) {
Assert.AreEqual(DBSimulation( i % 3, true), testCache.Get( i % 3));
logResult += String.Format("After {0} seconds => {1} returned from {2} \n",
((TimeSpan)(DateTime.Now - checkNow)).TotalSeconds,
testCache.Get( i % 3), i);
}
Console.WriteLine(logResult);
double elapsed = ((TimeSpan)(DateTime.Now - checkNow)).TotalSeconds;
Assert.LessOrEqual(elapsed, 15*3+1,"cache not working, {0} seconds elapsed", elapsed);
}
with its output
After 15,0035002 seconds => 1 returned from 1
After 30,0050002 seconds => 2 returned from 2
After 45,0065002 seconds => 0 returned from 3
After 45,0065002 seconds => 1 returned from 4
After 45,0065002 seconds => 2 returned from 5
...
After 45,0065002 seconds => 0 returned from 21
After 45,0065002 seconds => 1 returned from 22
After 45,0065002 seconds => 2 returned from 23
Edit
For a generic FromT an IEqualityComparer is needed for the Dictionary
Yes, not bad solution, but I offer you some improvements on concurrency(
ConcurrentDictionary
). You can use this code, for example, at your asp.net application where many threads simultaneously can use this class. Also as this class will be used for longrunning functions it will be good feature to not wait result but instead process result later(Task.ContinueWith
) when function will be completed:RESULT: