7

Predicate<Foo> を受け入れ、一致する項目のリストを返す 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() は、この目的のために適切な結果を生成しますか? 私が見るべきデリゲートの他のメンバーはいますか? これをまったく別の方法で行いますか?

4

5 に答える 5

4

キャッシュ タスクを実行するには、他の提案に従って、結果をキャッシュする Dictionary<Predicate<Foo>,List<Foo>> (グローバルの場合は静的、それ以外の場合はメンバー フィールド) を作成できます。Predicate<Foo> を実際に実行する前に、結果が辞書に既に存在するかどうかを確認する必要があります。

この決定論的関数キャッシングの一般的な名前はメモ化と呼ばれます - そしてその素晴らしい:)

C# 3.0 でラムダと Func/Action デリゲートのスワッグが追加されて以来、Memoization を C# に追加するのは非常に簡単です。

Wes Dyer は、いくつかの素晴らしい例を使用して、概念を C# に導入する素晴らしい投稿をしています。

これを行う方法を教えてほしい場合は、お知らせください...それ以外の場合は、Wes の投稿で十分です。

デリゲート ハッシュ コードに関するお問い合わせへの回答です。2 つのデリゲートが同じである場合、d1.GetHashCode() は d2.GetHashCode() と等しくなるはずですが、これについては 100% ではありません。メモ化を実行して、FindAll メソッドに WriteLine を追加することで、これをすばやく確認できます。これが当てはまらない場合、もう 1 つのオプションは、Linq.Expression<Predicate<Foo>> をパラメーターとして使用することです。式がクロージャでない場合、同じことを行う式は等しいはずです。

これがどうなるか教えてください。delegate.Equals についての答えを知りたいです。

于 2008-10-17T13:40:24.377 に答える
2

キャッシュされた結果をDictionary<Predicate<Foo>、List <Foo >>に保持するのは、すべての結果を永久にキャッシュするのではなく、ASP.NETキャッシュで有効期限を処理する必要があるため、私にとっては厄介ですが、それ以外の場合は良い解決策です。最終的には、WillのDictionary <Predicate <Foo>、string>を使用して、ASP.NETキャッシュキーで使用できる文字列をキャッシュすると思います。

いくつかの初期テストは、他の人が言っているように、デリゲートの平等が「正しいこと」を行うことを示唆していますが、Delegate.GetHashCodeは病理学的に役に立たないです。リフレクターが明らかに

public override int GetHashCode()
{
    return base.GetType().GetHashCode();
}

したがって、Predicate<Foo>は同じ結果を返します。

私の残りの問題は、匿名の代表者に対して平等がどのように機能するかでした。では、「同じターゲットで同じメソッドが呼び出される」とはどういう意味ですか?デリゲートが同じ場所で定義されている限り、参照は等しいようです。同じ体が異なる場所で定義されている代表者はそうではありません。

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つのメソッドは、キャッシュされた結果を共有しませんが、これはかなりまれなはずです。

于 2008-10-18T13:20:57.613 に答える
2

デリゲートの等価性は、呼び出しリスト内の各呼び出しを調べ、呼び出されるメソッドとメソッドのターゲットが等しいかどうかをテストします。

メソッドはキャッシュキーの単純な部分ですが、メソッドのターゲット (メソッドを呼び出すインスタンス - インスタンスメソッドを想定) をシリアライズ可能な方法でキャッシュすることは不可能な場合があります。特に、状態をキャプチャする無名関数の場合、その状態をキャプチャするために作成されたネストされたクラスのインスタンスになります。

これがすべてメモリ内にある場合は、ハッシュ キーとしてデリゲート自体を保持するだけで問題ありませんが、クライアントがガベージ コレクトされることを期待する一部のオブジェクトがハングアップすることを意味する場合があります。これをデータベースにシリアル化する必要がある場合は、さらに複雑になります。

メソッドがキャッシュ キー (文字列など) を受け入れるようにすることはできますか? (これは、メモリ内キャッシュが不十分であると想定しています。)

于 2008-10-17T13:23:15.643 に答える
1

Delegate の GetHashCode の実装が決定論的であり、衝突が発生しないことが確実でない限り、私はそれを信頼しません。

ここに2つのアイデアがあります。まず、デリゲートの結果を述語/リスト ディクショナリ内に格納し、述語をキーとして使用してから、キャッシュ内の単一のキーの下に結果のディクショナリ全体を格納します。悪いことに、キャッシュ アイテムが失われると、キャッシュされた結果がすべて失われます。

別の方法として、Predicate の拡張メソッドである GetKey() を作成し、オブジェクト/文字列ディクショナリを使用して、すべての Predicate のすべてのキーを格納および取得することもできます。デリゲートを使用して辞書にインデックスを付け、そのキーを返し、見つからない場合は作成します。このようにして、デリゲートごとに正しいキーを取得し、衝突がないことが保証されます。単純なものは型名 + Guid です。

于 2008-10-17T13:25:23.923 に答える
0

オブジェクトの同じインスタンスは、常に同じハッシュコードを返します (.Net では GetHashCode() の要件)。述語が静的リスト内にあり、毎回再定義しない場合、述語をキーとして使用しても問題はありません。

于 2008-10-18T05:06:26.787 に答える