キャッシングとメモ化に関するいくつかの記事と、デリゲートとジェネリックを使用して簡単に実装する方法を読んでいました。構文は非常に単純で、実装は驚くほど簡単ですが、繰り返しの性質があるため、同じ配管コードを何度も作成する代わりに、属性に基づいてコードを生成できるはずだと感じています。
デフォルトの例から始めたとしましょう:
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;
}
}
メモ化拡張メソッドの1つに一致するタグ付きメソッドが見つかったら、このコードを吐き出すコードジェネレーターを作成する方が簡単ではないかと思いました。したがって、この配管コードを記述する代わりに、属性を追加するだけで済みます。
class Foo
{
[Memoize]
public int Fibonacci(int n)
{
return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n;
}
}
正直なところ、これは実際のコード生成よりもプリプロセッサによって変換されるコンパイラシュガーのように見えることを私は知っていますが、私の質問は次のとおりです。
- 特定の属性を持つac#ソースファイル内のメソッドを見つけ、parametertypesとreturntypeを解析し、このフィンガープリントに一致するデリゲートを生成するための最良の方法は何だと思いますか
- 実際にコードを上書きせずに、これをビルドプロセスに統合するための最良の方法は何でしょうか。コンパイラに渡す前に、ソースファイルに対していくつかの前処理を行うことは可能ですか?
ありとあらゆるアイデアをありがとう。
更新:
Shayが提案したように、Postsharpライブラリを調べましたが、トランザクション管理、トレース、セキュリティなどのタイムクリティカルではないアプリケーションでの作業に非常に適しているようでした。
ただし、タイムクリティカルなコンテキストで使用すると、デリゲートよりもかなり遅いことがわかりました。実装ごとにフィボナッチの例を100万回繰り返すと、実行時間が80倍遅くなりました。(0.012msポストシャープvs 0.00015msデリゲート/コール)
しかし、正直なところ、私がそれを使用するつもりの文脈では、結果は完全に受け入れられます。回答ありがとうございます!
Update2:
どうやらPostsharpの作者はリリース2.0に一生懸命取り組んでおり、これにはとりわけ、生成されたコードのパフォーマンスの向上とコンパイル時間が含まれます。