デリゲート結果のキャッシュ
-
03-07-2019 - |
質問
Predicate <!> lt; Foo <!> gt;を受け入れるC#メソッドがあります。一致するアイテムのリストを返します...
public static List<Foo> FindAll( Predicate<Foo> filter )
{
...
}
多くの場合、フィルターは一般的なセットの1つになります...
public static class FooPredicates
{
public static readonly Predicate<Foo> IsEligible = ( foo => ...)
...
}
...しかし、匿名のデリゲートかもしれません。
このメソッドで結果をASP.NETキャッシュにキャッシュするようにしたいので、同じデリゲートで呼び出しを繰り返しても、キャッシュされた結果が返されるだけです。このために、デリゲートからキャッシュキーを作成する必要があります。 Delegate.GetHashCode()は、この目的のために適切な結果を生成しますか?デリゲートの他のメンバーを見てください。これを完全に別の方法で行いますか?
解決
キャッシングタスクを実行するには、他の提案に従って、辞書を作成します<!> lt; Predicate <!> lt; Foo <!> gt;、List <!> lt; Foo <!> gt; < !> gt; (グローバルの場合は静的、そうでない場合はメンバーフィールド)、結果をキャッシュします。 Predicate <!> lt; Foo <!> gt;を実際に実行する前に、結果が既に辞書に存在するかどうかを確認する必要があります。
この決定論的な関数のキャッシングの一般的な名前はメモ化と呼ばれます-その素晴らしい:)
C#3.0がラムダとFunc / Actionデリゲートのスワッグを追加して以来、C#へのメモ化の追加は非常に簡単です。
Wes Dyerには素晴らしい投稿これにより、いくつかの優れた例とともにC#に概念がもたらされます。
これを行う方法を教えてほしい場合はお知らせください...そうでなければ、Wesの投稿で十分でしょう。
デリゲートハッシュコードに関するクエリへの回答。 2つのデリゲートが同じ場合、d1.GetHashCode()はd2.GetHashCode()と等しくなりますが、これについては100%ではありません。メモ化を実行して、FindAllメソッドにWriteLineを追加すると、これをすばやく確認できます。これが真ではない場合、別のオプションはLinq.Expression <!> lt; Predicate <!> lt; Foo <!> gt; <!> gt;を使用することです。パラメータとして。式がクロージャでない場合、同じことを行う式は等しいはずです。
これがどのように行われるかを教えてください。デリゲートについての答えを知りたいです。等しいです。
他のヒント
デリゲートの等価性は、呼び出しリスト内の各呼び出しを調べ、呼び出されるメソッドとメソッドのターゲットの等価性をテストします。
メソッドはキャッシュキーの単純な部分ですが、メソッドのターゲット(呼び出し先のインスタンス-インスタンスメソッドを想定)は、シリアル化可能な方法でキャッシュすることが不可能な場合があります。特に、状態をキャプチャする匿名関数の場合、その状態をキャプチャするために作成されたネストされたクラスのインスタンスになります。
これがすべてメモリ内にある場合、デリゲート自体をハッシュキーとして保持するだけで問題ありません-ただし、クライアントがガベージコレクションを期待している一部のオブジェクトがハングアップする可能性があります。これをデータベースにシリアル化する必要がある場合、より複雑になります。
メソッドでキャッシュキー(文字列など)も受け入れますか? (メモリ内キャッシュが不十分であると仮定しています。)
キャッシュされた結果をディクショナリに保持する<!> lt; Predicate <!> lt; Foo <!> gt;、List <!> lt; Foo <!> gt; <!> gt;すべての結果を永久にキャッシュするのではなく、ASP.NETキャッシュで有効期限を処理したいので、私にとっては厄介ですが、それ以外の場合は良い解決策です。 Will's Dictionary <!> lt; Predicate <!> lt; Foo <!> gt;、string <!> gt; ASP.NETキャッシュキーで使用できる文字列をキャッシュします。
初期テストのいくつかは、デリゲートの平等が<!> quot;正しいこと<!> quot;を行うことを示唆しています。他の人が言ったように、Delegate.GetHashCodeは病理学的に役に立たない。リフレクターが明らかに
public override int GetHashCode()
{
return base.GetType().GetHashCode();
}
したがって、述語<!> lt; Foo <!> gt;同じ結果を返します。
残りの問題は、匿名のデリゲートで平等がどのように機能するかでした。 <!> quot;同じターゲットで同じメソッドが呼び出されること<!> quot;どういう意味?デリゲートが同じ場所で定義されている限り、参照は等しいようです。異なる場所で定義された同じボディを持つデリゲートはそうではありません。
static Predicate<int> Test()
{
Predicate<int> test = delegate(int i) { return false; };
return test;
}
static void Main()
{
Predicate<int> test1 = Test();
Predicate<int> test2 = Test();
Console.WriteLine(test1.Equals( test2 )); // True
test1 = delegate(int i) { return false; };
test2 = delegate(int i) { return false; };
Console.WriteLine(test1.Equals( test2 )); // False
}
これは私の必要に応じて問題ないはずです。事前定義された述部を持つ呼び出しはキャッシュされます。匿名メソッドでFindAllを呼び出す1つのメソッドを複数回呼び出すと、キャッシュされた結果が得られます。明らかに同じ匿名メソッドでFindAllを呼び出す2つのメソッドは、キャッシュされた結果を共有しませんが、これはかなりまれです。
DelegateのGetHashCodeの実装が確定的であり、衝突が発生しないと確信しない限り、私はそれを信用しません。
これは2つのアイデアです。最初に、述語をキーとして使用して、デリゲートの結果を述語/リストディクショナリに格納し、次にキャッシュ内の単一のキーの下に結果のディクショナリ全体を格納します。悪いことは、キャッシュアイテムが失われると、キャッシュされた結果がすべて失われることです。
別の方法は、オブジェクト/文字列辞書を使用してすべてのPredicateのすべてのキーを保存および取得する、Predicateの拡張メソッドGetKey()を作成することです。デリゲートを使用して辞書にインデックスを付け、そのキーを返します。見つからない場合は作成します。これにより、デリゲートごとに正しいキーが取得され、衝突が発生しないことが保証されます。ナイーブなものは、タイプ名+ Guidです。
オブジェクトの同じインスタンスは、常に同じハッシュコードを返します(.NetのGetHashCode()の要件)。述語が静的リスト内にあり、毎回再定義していない場合、それらをキーとして使用することに問題はありません。