我正在阅读一些关于缓存和记忆的文章,以及如何使用委托和泛型轻松实现它。语法非常简单,实现起来非常简单,但我觉得由于重复性,应该可以基于属性生成代码,而不必反复编写相同的管道代码。
假设我们从默认示例开始:
class Foo
{
public int Fibonacci(int n)
{
return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n;
}
}
然后记住这个:
// Let's say we have a utility class somewhere with the following extension method:
// public static Func<TResult> Memoize<TResult>(this Func<TResult> f)
class Foo
{
public Func<int,int> Fibonacci = fib;
public Foo()
{
Fibonacci = Fibonacci.Memoize();
}
public int fib(int n)
{
return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n;
}
}
我想,一旦它找到一个与Memoize扩展方法之一匹配的标记方法,那么只需创建一个吐出这段代码的代码生成器就不会更简单了。因此,我只需要添加一个属性:
,而不是编写这个管道代码class Foo
{
[Memoize]
public int Fibonacci(int n)
{
return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n;
}
}
老实说,我知道这看起来更像是应该由预处理器转换而不是实际代码生成的编译器糖,但我的问题是:
感谢您提出的所有想法。
更新:
我已经按照Shay的建议调查了Postsharp库,它似乎非常适合交易管理,跟踪或安全等非时间关键型应用程序。
然而,当在时间关键的上下文中使用它时,它证明比委托更慢。每次实现的Fibonacci示例的一百万次迭代导致运行时间减慢80倍。 (0.012ms postharp vs。每次通话0.00015ms代表)
但老实说,在我打算使用它的上下文中,结果是完全可以接受的。感谢您的回复!
UPDATE2 :
显然,Postsharp的作者正在努力研究release 2.0,其中包括生成的代码中的性能改进和编译时间。
答案 0 :(得分:6)
答案 1 :(得分:3)
我在我的一个项目中使用了以下Memoize函数:
public class Foo
{
public int Fibonacci(int n)
{
return n > 1 ? Fibonacci(n - 1) + Fibonacci(n - 2) : n;
}
}
class Program
{
public static Func<Т, TResult> Memoize<Т, TResult>(Func<Т, TResult> f) where Т : IEquatable<Т>
{
Dictionary<Т, TResult> map = new Dictionary<Т, TResult>();
return a =>
{
TResult local;
if (!TryGetValue<Т, TResult>(map, a, out local))
{
local = f(a);
map.Add(a, local);
}
return local;
};
}
private static bool TryGetValue<Т, TResult>(Dictionary<Т, TResult> map, Т key, out TResult value) where Т : IEquatable<Т>
{
EqualityComparer<Т> comparer = EqualityComparer<Т>.Default;
foreach (KeyValuePair<Т, TResult> pair in map)
{
if (comparer.Equals(pair.Key, key))
{
value = pair.Value;
return true;
}
}
value = default(TResult);
return false;
}
static void Main(string[] args)
{
var foo = new Foo();
// Transform the original function and render it with memory
var memoizedFibonacci = Memoize<int, int>(foo.Fibonacci);
// memoizedFibonacci is a transformation of the original function that can be used from now on:
// Note that only the first call will hit the original function
Console.WriteLine(memoizedFibonacci(3));
Console.WriteLine(memoizedFibonacci(3));
Console.WriteLine(memoizedFibonacci(3));
Console.WriteLine(memoizedFibonacci(3));
}
}
在我的项目中,我只需要使用一个实现IEquatable<Т>的参数的函数,但这可以进一步推广。 另一个重要的评论是这段代码不是线程安全的。如果需要线程安全性,则需要同步对内部映射哈希表的读/写访问权限。
答案 2 :(得分:1)
专门针对您的观点:
答案 3 :(得分:1)
如果您向PostSharp编写插件而不是使用其LAOS库,则不会受到性能影响。