2009-11-10 10 views
9

Il metodo di estensione IEnumerable FirstOrDefault non esattamente fare come volevo così ho creato FirstOrValue. E 'un buon modo per fare questo o c'è un modo migliore?un approccio diverso sul FirstOrDefault

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, T value) 
{ 
    T first = source.FirstOrDefault(predicate); 
    return Equals(first, default(T)) ? value : first; 
} 

risposta

39

Il tuo codice è probabilmente errato; probabilmente non hai considerato tutti i casi.

Naturalmente, non possiamo sapere se esiste il codice è corretto o non corretto finché non avremo uno spec. Quindi inizia scrivendo una specifica su una riga:

"FirstOrValue<T> prende una sequenza di T, un predicato e un valore di T, e restituisce il primo elemento nella sequenza che corrisponde al predicato se ce n'è uno, oppure , se non c'è, il valore dichiarato. "

Il vostro tentativo effettivamente attuare tale spec? Certamente no! Provalo:

int x = FirstOrValue<int>(new[] { -2, 0, 1 }, y=>y*y==y, -1); 

restituisce -1. La risposta corretta in base alle specifiche è 0. Il primo elemento che corrisponde al predicato è zero, quindi deve essere restituito.

Una corretta applicazione delle specifiche sarà simile:

public static T FirstOrValue<T>(this IEnumerable<T> sequence, Func<T, bool> predicate, T value) 
{ 
    if (sequence == null) throw new ArgumentNullException("sequence"); 
    if (predicate == null) throw new ArgumentNullException("predicate"); 
    foreach(T item in sequence) 
     if (predicate(item)) return item; 
    return value; 
} 

Sempre scrivere una specifica prima, anche se è solo una sola frase.

+0

Questo dovrebbe essere parte del Framework! O l'ho trascurato? – Marcel

+2

+1 per "Scrivi sempre una specifica prima". –

5

default(T) tornerà null per impostazione predefinita per i tipi di riferimento.

farei questo

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, T value) 
{ 
    T first = source.FirstOrDefault(predicate); 
    return first ?? value; 
} 
+4

mi limiterò a scrivere su una sola riga: ritorno source.FirstOrDefault (predicato) ?? valore; – Zote

+0

punti extra per nulla coalescenza –

+1

@zote: facendo così Daniels rende più facile per collegare un debugger e vedere cosa sta succedendo –

-1

mi sembra ragionevole se si desidera modificare la leggibilità invece di utilizzare DefaultIfEmpty.

Si potrebbe anche creare un override che utilizza una lambda se la creazione del valore di default è costoso, creandola solo se necessario.

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, Func<T> getValue) 
{ 
    T first = source.FirstOrDefault(predicate); 
    return Equals(first, default(T)) ? getValue() : first; 
} 
+0

Molto bello, l'aggiunta di una lambda lo rende ancora più utile. – Aurequi

0

Poiché si tratta di un sovraccarico, vale la pena menzionare la versione senza predicato.

public static T FirstOrValue<T>(this IEnumerable<T> sequence, T value) 
{ 
    if (sequence == null) throw new ArgumentNullException("sequence"); 
    foreach(T item in sequence) 
     return item; 
    return value; 
} 
+2

Supponiamo che la sequenza contenga un milione di elementi e nessuna proprietà Count. Dovresti enumerare * tutti * di loro per determinare che non ce ne sono * nessuno *. Se hai un milione di penny in un barattolo e vuoi sapere se il barattolo è vuoto, conti i penny? Questa è una tecnica molto, molto cattiva che stai usando qui. (Non è chiaro anche per quale motivo stai postando le risposte a una domanda di sei anni che ha già una risposta corretta accettata e in effetti non rispondi nemmeno alla domanda originale, forse c'è un altro modo che potresti aggiungere più valore al sito? –

+0

Stavo cercando FirstOrValue e ho trovato la tua bella risposta. Ho copiato il tuo codice e mi è venuto in mente che c'è un sovraccarico. Incollato qui nel caso in cui altri hanno attraversato lo stesso processo di pensiero. Forse potresti suggerire un modo migliore per farlo. – djv

+1

Bene, dal momento che il metodo proposto è uguale a quello che ho scritto tranne senza un predicato, il mio suggerimento sarebbe quello di scrivere lo stesso metodo senza un predicato: 'public static T FirstOrValue (questa sequenza IEnumerable , valore T) { if (sequence == null) lancia una nuova ArgumentNullException ("sequence"); foreach (articolo T in sequenza) articolo restituito; valore restituito; } ' –

Problemi correlati