2009-11-10 8 views
135

Sono molto nuovo a utilizzare predicati e appena imparato a scrivere:Che cos'è un predicato in C#?

Predicate<int> pre = delegate(int a){ a %2 == 0 }; 

Quale sarà il ritorno del predicato, e come è utile durante la programmazione?

+13

Ecco il mio articolo su Dr. Pepper, predicati e funzioni di sicurezza: http://blogs.msdn.com/ericlippert/archive/2008/08/19/tasty-beverages.aspx –

+2

@ 0A0D: penso è una cattiva idea etichettare una domanda ".net-2.0" se la domanda non è specifica per quella versione. Basta tag ".net". –

+0

@ John: concordato. Aveva 2.0 nell'originale, quindi stavo semplicemente unendo .net e 2.0 in .net-2.0. –

risposta

380

Predicate<T> è un costrutto funzionale che fornisce un modo pratico di testare fondamentalmente se qualcosa è vero per un dato oggetto T.

Per esempio supponiamo di avere una classe:

class Person { 
    public string Name { get; set; } 
    public int Age { get; set; } 
} 

Ora diciamo che ho un List<Person> people e voglio sapere se c'è qualcuno di nome Oscar nella lista.

Senza utilizzando un Predicate<Person> (o LINQ, o qualsiasi di quella roba di fantasia), ho sempre potuto fare questo nel modo seguente:

Person oscar = null; 
foreach (Person person in people) { 
    if (person.Name == "Oscar") { 
     oscar = person; 
     break; 
    } 
} 

if (oscar != null) { 
    // Oscar exists! 
} 

Questo va bene, ma poi diciamo che voglio controlla se c'è una persona chiamata "Ruth"? O una persona di età inferiore ai 17 anni?

Utilizzando un Predicate<Person>, posso trovare queste cose usando molto meno codice:

Predicate<Person> oscarFinder = (Person p) => { return p.Name == "Oscar"; }; 
Predicate<Person> ruthFinder = (Person p) => { return p.Name == "Ruth"; }; 
Predicate<Person> seventeenYearOldFinder = (Person p) => { return p.Age == 17; }; 

Person oscar = people.Find(oscarFinder); 
Person ruth = people.Find(ruthFinder); 
Person seventeenYearOld = people.Find(seventeenYearOldFinder); 

Avviso ho detto un sacco meno codice, non molto più veloce . Uno sviluppatore erroneo comune è che se qualcosa prende una riga, deve eseguire meglio di qualcosa che richiede dieci righe. Ma dietro le quinte, il metodo Find, che prende un Predicate<T>, è solo un elenco dopo tutto. Lo stesso vale per molte delle funzionalità di Linq.

Quindi, diamo uno sguardo al codice specifico nella tua domanda:

Predicate<int> pre = delegate(int a){ return a % 2 == 0; }; 

Qui abbiamo una Predicate<int> pre che prende un int a e restituisce a % 2 == 0. Questo è essenzialmente il test per un numero pari. Ciò significa:

pre(1) == false; 
pre(2) == true; 

E così via. Questo significa anche, se si dispone di un List<int> ints e si desidera trovare il primo numero pari, si può solo fare questo:

int firstEven = ints.Find(pre); 

Naturalmente, come con qualsiasi altro tipo che è possibile utilizzare nel codice, è una buona idea di dare i nomi descrittivi delle tue variabili; quindi consiglierei di cambiare il precedente pre in qualcosa come evenFinder o isEven - qualcosa di simile. Poi il codice di cui sopra è molto più chiaro:

int firstEven = ints.Find(evenFinder); 
+30

+1 da parte mia! Di gran lunga la risposta più chiara sulla pagina. –

+3

+1: molto ben scritto. Vorrei solo aggiungere il modulo di un singolo MSDN: "Rappresenta il metodo che definisce un insieme di criteri e determina se l'oggetto specificato soddisfa tali criteri" –

+0

sorprendente. preferendo anche la domanda. –

34

Il predicato restituirà sempre un valore booleano, per definizione.

Predicate<T> è praticamente identico a Func<T,bool>.

I predicati sono molto utili nella programmazione. Vengono spesso utilizzati per consentire di fornire la logica in fase di esecuzione, che può essere semplice o complicata quanto necessario.

Ad esempio, WPF utilizza un Predicate<T> come input per il filtraggio di ICollectionView di ListView. Ciò consente di scrivere una logica che può restituire un valore booleano che determina se un elemento specifico debba essere incluso nella vista finale. La logica può essere molto semplice (basta restituire un valore booleano sull'oggetto) o molto complessa, tutto a te.

+0

Solo un peccato non lo sono :) – leppie

+7

I delegati sono utili nella programmazione. Francamente, trovo il nome Predicate molto inutile, come dimostra chiaramente questa domanda. Se vuoi davvero descrivere che predicato lo chiameresti Filtro . – ChaosPandion

+1

Non cosa? [15 caratteri] –

11

In C# I predicati sono semplicemente dei delegati che restituiscono i booleani. Sono utili (nella mia esperienza) quando stai cercando attraverso una collezione di oggetti e vuoi qualcosa di specifico.

Ultimamente li ho incontrati usando i controlli web di terze parti (come le treeview), quindi quando ho bisogno di trovare un nodo all'interno di un albero, uso il metodo .Find() e passo un predicato che restituirà lo specifico nodo che sto cercando. Nel tuo esempio, se 'a' mod 2 è 0, il delegato restituirà true. Certo, quando cerco un nodo in una vista ad albero, confronto il nome, il testo e le proprietà del valore per una corrispondenza. Quando il delegato trova una corrispondenza, restituisce il nodo specifico che stavo cercando.

13

Il codice seguente può aiutare a capire un po 'di uso reale mondo di predicati (Combinato con iteratori di nome).

namespace Predicate 
{ 
    class Person 
    { 
     public int Age { get; set; } 
    } 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      foreach (Person person in OlderThan(18)) 
      { 
       Console.WriteLine(person.Age); 
      } 
     } 

     static IEnumerable<Person> OlderThan(int age) 
     { 
      Predicate<Person> isOld = x => x.Age > age; 
      Person[] persons = { new Person { Age = 10 }, new Person { Age = 20 }, new Person { Age = 19 } }; 

      foreach (Person person in persons) 
       if (isOld(person)) yield return person; 
     } 
    } 
} 
+1

In alternativa, invece di foreach/yield loop puoi "return person.FindAll (isOld);". –

Problemi correlati