Domanda

Ho Dictionary<Predicate<double>, SomeEnum>:

var dic = new Dictionary<Predicate<double>, SomeEnum>
{
    { (d) => d < 10, SomeEnum.Foo },
    { (d) => d > 90, SomeEnum.Bar }
};

Voglio chiamare TryGetValue(K, out V) contro di essa in questo modo:

dic.TryGetValue(99)

e ricevere

SomeStruct.Bar

Ma prima param per TryGetValue() è Predicate<T>, non solo T. Come posso fare quello che voglio?

Ho trovato solo una soluzione sporca:

var kpv = dic.FirstOrDefault(p => p.Key(99));
if (kpv.Key != null)
    var result = kpv.Value;

Ci sono altri modi?

o come attuare la mia idea correttamente -?. Dichiarare una chiave non come una costante, ma come un segmento

È stato utile?

Soluzione

Ci sono un paio di cose sbagliate qui:

Predicate<double> non è un tipo adatto da utilizzare come TKey. La chiave per un dizionario si suppone che individuare un valore, non calcolare un valore.

Questo non avrebbe alcun senso utilizzando lambda sia. Perché sono anonimi, non si ottiene alcuna equivalenza, e non sarà in grado di utilizzare un dizionario.

Vedere questo esempio di codice per un esempio:

Predicate<double> fn_1 = d => d == 34.0d;
Predicate<double> fn_2 = d => d == 34.0d;

// Note: There are not equal
if (fn_1 == fn_2)
    Console.WriteLine("These are Equal?");

Se non altro, è possibile utilizzare un elenco di delegati ed eseguire a ciascuno di trovare quelli che incontro, ma a quel punto si deve aspettare più risultati. Se si desidera solo per ottenere un unico risultato, allora è necessario considerare che ordine i predicati sono memorizzate all'interno della vostra lista.

Do not abuso KeyValuePair come un hack per non avere Tuple<T1,T2>. Sarebbe abbastanza facile creare una classe che ha sia un predicato ed un SomeStruct. Guardate:

public class MySegment
{   
     public Predicate<double> Predicate {get;set;}
     public SomeStruct Result {get;set;}
}

Per passare attraverso una sequenza di predicati, e trovare quelli corrispondenti sarebbe simile a questa:

...
List<MySegment> list = new List<MySegment>();
...
list.Add(new MySegment { Predicate = d => d < 10, Result = SomeStruct.Foo });
list.Add(new MySegment { Predicate = d => d > 90, Result = SomeStruct.Bar });

...

public IEnumerable<SomeStruct> GetResults(double input)
{ 
    foreach (var item in list)
        if (item.Predicate(input))
             yield return item.Result;
}

Altri suggerimenti

Se l'elenco dei predicati non è troppo lungo, si può semplicemente aggiungerli a una List<KeyValuePair<Predicate<T>, V>> e quindi si esegue una query LINQ:

var lt10 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d < 10, SomeStruct.Foo);
var gt90 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d > 90, SomeStruct.Bar);
var predicates = new List<KeyValuePair<Predicate<Double>, SomeStruct>>() { lt10, gt90 };

var result = predicates.FirstOrDefault(p => p.Key(99));

È meglio utilizzare SomeStruct? invece di SomeStruct, inoltre, da allora FirstOrDefault darà un risultato inequivocabile se non corrisponde a nessuna.

Se l'elenco è molto lungo, si vuole prendere in considerazione un qualche tipo di struttura dati che permette di query su una serie, come un Interval Albero .

Questo non può essere fatto utilizzando un dizionario, perché si basa su valori hash per determinare rapidamente dove cercare un determinato tasto.

Come hai scoperto, è possibile richiamare i predicati direttamente, ma che richiederà funzioni per essere chiamato O (n), che non è migliore rispetto all'utilizzo di un elenco, o anche un grande if / then / else è.

Se la vostra collezione di potenziali predicati è troppo lungo per questo di essere un'opzione, è necessario creare la propria struttura dei dati per soddisfare i vostri scopi. Se si sta solo pensando di definire i valori in base a intervalli interi, questo non dovrebbe essere difficile, ma potrebbe sfuggire di mano se il vostro predicati diventano più complessi.

Una nota a parte, il linguaggio F #, che ha un supporto integrato per questo tipo di definizione utilizzando Partita Espressioni . Non so come va sulla compilazione dei rami, ma suppongo che sia abbastanza intelligente su di esso.

Modifica

Ecco un esempio di utilizzo di un espressione Partita in F # per qualcosa di simile:

// Define the "choose" function
let choose value = 
    match value with
    | v when v < 10 -> 1
    | v when v > 90 -> 2
    | _ -> 0

// Test the "choose" function
let choice1 = choose 5
let choice2 = choose 15
let choice3 = choose 95

Il codice sopra ottengono i seguenti valori:

choice1 = 1 
choice2 = 0 
choice3 = 2

Non ho mai effettivamente lavorato con F # prima, quindi si dovrà guardarsi intorno per come utilizzare una funzione da F # in un programma C #.

Si dovrà scorrere i criteri e gestito ogni predicato contro l'ingresso per vedere se corrisponde. Non vedo alcuna ragione per usare un dizionario qui.

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