Question

J'ai une méthode C # qui accepte un prédicat < Foo > et renvoie une liste des éléments correspondants ...

public static List<Foo> FindAll( Predicate<Foo> filter )
{
    ...
}

Le filtre fera souvent partie d'un ensemble commun ...

public static class FooPredicates
{
    public static readonly Predicate<Foo> IsEligible = ( foo => ...)
    ...
}

... mais peut être un délégué anonyme.

J'aimerais maintenant que cette méthode mette en cache ses résultats dans le cache ASP.NET. Par conséquent, les appels répétés avec le même délégué renvoient simplement le résultat mis en cache. Pour cela, je dois créer une clé de cache à partir du délégué. Delegate.GetHashCode () produira-t-il des résultats sensibles à cette fin? Y a-t-il un autre membre du délégué que je devrais regarder? Feriez-vous cela d'une toute autre manière?

Était-ce utile?

La solution

Pour effectuer votre tâche de mise en cache, vous pouvez suivre les autres suggestions et créer un dictionnaire < Predicate < Foo >, List < Foo > < !> gt; (statique pour global ou autre champ membre) qui met en cache les résultats. Avant d’exécuter réellement le prédicat & Lt; Foo & Gt ;, vous devez vérifier si le résultat existe déjà dans le dictionnaire.

Le nom général de la mise en cache de cette fonction déterministe est appelé mémorisation - et son nom génial:)

Depuis que C # 3.0 a ajouté le lambda et le swag des délégués Func / Action, l'ajout de la mémorisation à C # est très facile.

Wes Dyer a publié un article génial cela amène le concept à C # avec quelques bons exemples.

Si vous voulez que je vous montre comment faire cela, faites-le-moi savoir ... sinon, le message de Wes devrait suffire.

En réponse à votre question sur les codes de hachage des délégués. Si deux délégués sont identiques, d1.GetHashCode () doit être égal à d2.GetHashCode (), mais je ne suis pas à 100% à ce sujet. Vous pouvez vérifier cela rapidement en essayant Memoization et en ajoutant un WriteLine à votre méthode FindAll. Si cela finit par ne pas être vrai, une autre option consiste à utiliser Linq.Expression & Lt; Prédicat & Lt; Foo & Gt; & Gt; en paramètre. Si les expressions ne sont pas des fermetures, les expressions qui font la même chose doivent être égales.

Faites-moi savoir comment cela se passe, je suis intéressé à connaître la réponse à propos de delegate.Equals.

Autres conseils

Déléguer l’égalité examine chaque appel dans la liste d’invocations, teste l’égalité de la méthode à appeler et la cible de la méthode.

La méthode est un simple élément de la clé de cache, mais la cible de la méthode (l'instance pour l'appeler - en supposant une méthode d'instance) pourrait être impossible à mettre en cache de manière sérialisable. En particulier, pour les fonctions anonymes qui capturent l’état, ce sera une instance d’une classe imbriquée créée pour capturer cet état.

Si tout cela est en mémoire, il suffit de garder le délégué lui-même car la clé de hachage sera correcte - bien que cela puisse signifier que certains objets que les clients s'attendent à ce qu'ils soient ramassés à la poubelle traînent. Si vous avez besoin de le sérialiser dans une base de données, cela devient plus épais.

Pourriez-vous obliger votre méthode à accepter également une clé de cache (une chaîne, par exemple)? (Cela suppose qu'un cache en mémoire est inadéquat.)

Conserver les résultats mis en cache dans un dictionnaire < Prédicat < Foo >, Liste < Foo > > Cela me gêne, car je veux que le cache ASP.NET gère l’expiration pour moi plutôt que de mettre tous les résultats en cache pour toujours, mais c’est une bonne solution. Je pense que je vais finir par utiliser le dictionnaire de Will & Lt; Predicate & Lt; Foo & Gt;, string & Gt; mettre en cache une chaîne que je peux utiliser dans la clé de cache ASP.NET.

Certains tests initiaux suggèrent que l'égalité de délégation est la & "bonne chose &"; comme d'autres l'ont dit, Delegate.GetHashCode est pathologiquement inutile. Le réflecteur révèle

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

Donc tout prédicat < Foo > renvoie le même résultat.

Mon dernier problème était de savoir comment l'égalité fonctionnait pour les délégués anonymes. Qu'est-ce que & "; Même méthode appelée sur la même cible &"; signifie alors? Il semble que tant que le délégué a été défini au même endroit, les références sont égales. Les délégués ayant le même corps défini à des endroits différents ne le sont pas.

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
}

Cela devrait convenir à mes besoins. Les appels avec les prédicats prédéfinis seront mis en cache. Les appels multiples à une méthode qui appelle FindAll avec une méthode anonyme doivent obtenir des résultats mis en cache. Deux méthodes appelant FindAll avec apparemment la même méthode anonyme ne partageront pas les résultats mis en cache, mais cela devrait être assez rare.

Sauf si vous êtes sûr que l'implémentation de GetHashCode par Delegate est déterministe et n'entraîne aucune collision, je ne lui ferais pas confiance.

Voici deux idées. Commencez par stocker les résultats des délégués dans un dictionnaire prédicat / liste, en utilisant le prédicat comme clé, puis stockez le dictionnaire entier des résultats sous une clé unique dans le cache. La mauvaise chose est que vous perdez tous vos résultats mis en cache si l'élément de cache est perdu.

Une autre solution consisterait à créer une méthode d’extension pour Predicate, GetKey (), qui utilise un dictionnaire objet / chaîne pour stocker et récupérer toutes les clés de tous les prédicats. Vous indexez dans le dictionnaire avec le délégué et renvoyez sa clé, en créant une si vous ne la trouvez pas. De cette façon, vous êtes assuré d'obtenir la bonne clé par délégué et il n'y a pas de collision. Un nom naïf serait le nom de type + Guid.

La même instance d'un objet retournera toujours le même hashcode (exigence de GetHashCode () en .Net). Si vos prédicats figurent dans une liste statique et que vous ne les redéfinissez pas à chaque fois, je ne vois pas de problème à les utiliser comme clés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top