Domanda

Ho un metodo C # che accetta un Predicato < Foo > e restituisce un elenco di elementi corrispondenti ...

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

Il filtro sarà spesso uno di un set comune ...

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

... ma potrebbe essere un delegato anonimo.

Ora vorrei che questo metodo memorizzasse i risultati nella cache ASP.NET, quindi le chiamate ripetute con lo stesso delegato restituiscono il risultato memorizzato nella cache. Per questo, ho bisogno di creare una chiave cache dal delegato. Delegate.GetHashCode () produrrà risultati sensibili a questo scopo? C'è qualche altro membro del delegato che dovrei guardare? Lo faresti in un altro modo?

È stato utile?

Soluzione

Per eseguire l'attività di memorizzazione nella cache, è possibile seguire gli altri suggerimenti e creare un dizionario < Predicate < Foo >, elenco < Foo > < !> gt; (statico per il campo globale o membro altrimenti) che memorizza nella cache i risultati. Prima di eseguire effettivamente il Predicato & Lt; Foo & Gt ;, dovresti verificare se il risultato esiste già nel dizionario.

Il nome generale di questa funzione deterministica nella cache si chiama Memoization - ed è fantastico :)

Da quando C # 3.0 ha aggiunto lambda e lo swag di delegati Func / Action, aggiungere Memoization a C # è abbastanza semplice.

Wes Dyer ha un ottimo post che porta il concetto in C # con alcuni grandi esempi.

Se vuoi che ti mostri come fare, fammi sapere ... altrimenti, il post di Wes dovrebbe essere adeguato.

In risposta alla tua domanda sui codici hash delegati. Se due delegati sono uguali, d1.GetHashCode () dovrebbe essere uguale a d2.GetHashCode (), ma non sono al 100% a riguardo. Puoi verificarlo rapidamente dando Memoization e aggiungendo un WriteLine al tuo metodo FindAll. Se ciò non dovesse essere vero, un'altra opzione è usare Linq.Expression & Lt; Predicate & Lt; Foo & Gt; & Gt; come parametro Se le espressioni non sono chiusure, le espressioni che fanno la stessa cosa dovrebbero essere uguali.

Fammi sapere come va, sono interessato a conoscere la risposta su delegate.Equals.

Altri suggerimenti

L'uguaglianza dei delegati esamina ogni invocazione nell'elenco di invocazioni, verificando l'uguaglianza del metodo da invocare e la destinazione del metodo.

Il metodo è un semplice pezzo della chiave cache, ma la destinazione del metodo (l'istanza su cui richiamarlo - presupponendo un metodo di istanza) potrebbe essere impossibile memorizzare nella cache in modo serializzabile. In particolare, per le funzioni anonime che acquisiscono lo stato, sarà un'istanza di una classe nidificata creata per acquisire tale stato.

Se tutto ciò è in memoria, tenere semplicemente il delegato stesso come la chiave hash andrà bene, anche se ciò può significare che alcuni oggetti che i clienti si aspetterebbero di essere raccolti in modo inutile restano in giro. Se devi serializzare questo su un database, diventa più peloso.

Potresti far accettare al tuo metodo anche una chiave di cache (ad es. una stringa)? (Ciò presuppone che una cache in memoria sia inadeguata.)

Mantenere i risultati memorizzati nella cache in un dizionario < Predicato < Foo >, Elenco < Foo > > è imbarazzante per me perché voglio che la cache ASP.NET gestisca la scadenza per me piuttosto che memorizzare nella cache tutti i risultati per sempre, ma per il resto è una buona soluzione. Penso che finirò con il dizionario di Will & Lt; Predicate & Lt; Foo & Gt;, string & Gt; memorizzare nella cache una stringa che posso usare nella chiave cache ASP.NET.

Alcuni test iniziali suggeriscono che l'uguaglianza dei delegati fa il " cosa giusta " come altri hanno già detto, ma Delegate.GetHashCode è patologicamente inutile. Riflettore rivela

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

Quindi qualsiasi Predicato < Foo > restituisce lo stesso risultato.

Il mio problema rimanente era come funziona l'uguaglianza per i delegati anonimi. Cosa significa & Quot; stesso metodo chiamato sullo stesso target & Quot; intendi allora? Sembra che fino a quando il delegato è stato definito nello stesso posto, i riferimenti siano uguali. I delegati con lo stesso corpo definito in luoghi diversi non lo sono.

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
}

Questo dovrebbe essere OK per le mie esigenze. Le chiamate con predicati predefiniti verranno memorizzate nella cache. Più chiamate a un metodo che chiama FindAll con un metodo anonimo dovrebbero ottenere risultati memorizzati nella cache. Due metodi che chiamano FindAll con apparentemente lo stesso metodo anonimo non condivideranno i risultati memorizzati nella cache, ma questo dovrebbe essere abbastanza raro.

A meno che tu non sia sicuro che l'implementazione di GetHashCode da parte del delegato sia deterministica e non comporti alcuna collisione di cui non mi fiderei.

Ecco due idee. Innanzitutto, archiviare i risultati dei delegati all'interno di un dizionario di predicato / elenco, utilizzando il predicato come chiave, quindi archiviare l'intero dizionario dei risultati in un'unica chiave nella cache. La cosa brutta è che perdi tutti i risultati memorizzati nella cache se l'elemento cache viene perso.

Un'alternativa sarebbe quella di creare un metodo di estensione per Predicate, GetKey (), che utilizza un dizionario oggetto / stringa per archiviare e recuperare tutte le chiavi per tutti i predicati. Indicizzi nel dizionario con il delegato e restituisci la sua chiave, creandone una se non la trovi. In questo modo hai la certezza di ottenere la chiave corretta per delegato e non ci sono collisioni. Un naiive sarebbe digitare name + Guid.

La stessa istanza di un oggetto restituirà sempre lo stesso hashcode (requisito di GetHashCode () in .Net). Se i tuoi predicati sono all'interno di un elenco statico e non li stai ridefinendo ogni volta, non riesco a vedere un problema nell'usarli come chiavi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top