Quando utilizzare .First e quando utilizzare .FirstOrDefault con LINQ?


824

Ho cercato in giro e non ho davvero trovato una risposta chiara su quando si desidera utilizzare .Firste quando si desidera utilizzare .FirstOrDefaultcon LINQ.

  • Quando vorresti usare .First? Solo quando desideri catturare l'eccezione se non vengono restituiti risultati?

    var result = List.Where(x => x == "foo").First();
  • E quando vorresti usare .FirstOrDefault? Quando vorresti sempre il tipo predefinito se nessun risultato?

    var result = List.Where(x => x == "foo").FirstOrDefault();
  • E del resto, che dire di Take?

    var result = List.Where(x => x == "foo").Take(1);

86
.Firsted .FirstOrDefaultentrambi considerano i predicati come argomenti, quindi var result = List.Where(x => x == "foo").First();potrebbero essere riscritti comevar result = List.First(x => x == "foo");
Rian Schmits il

59
Non dimenticare di considerare Singlee SingleOrDefault. Odio quando le persone usano Firstquando vogliono veramente Single; )
BartoszKP

19
Single o SingleOrDefault genererebbe un'eccezione se viene restituito più di un elemento! Penso che FirstOrDefault sia migliore nei casi più comuni!
Eric Draven,

21
Il punto è quando ti aspetti un singolo risultato, dovresti dirlo e l'eccezione indica che la tua logica non è riuscita.
NetMage,

1
Considera anche che l'utilizzo .FirstOrDefault()ti dà sempre l'opportunità di generare un'eccezione più significativa. Se viene generata un'eccezione di sequenza e più di una .First()in un metodo, può essere difficile discernere quale affermazione è il problema.
StingyJack,

Risposte:


807

Vorrei usare First()quando conosco o mi aspetto che la sequenza abbia almeno un elemento. In altre parole, quando è un evento eccezionale la sequenza è vuota.

Utilizzare FirstOrDefault()quando si sa che sarà necessario verificare se esiste o meno un elemento. In altre parole, quando è legale che la sequenza sia vuota. Non fare affidamento sulla gestione delle eccezioni per il controllo. (È una cattiva pratica e potrebbe danneggiare le prestazioni).

Infine, la differenza tra First()e Take(1)è che First()restituisce l'elemento stesso, mentre Take(1)restituisce una sequenza di elementi che contiene esattamente un elemento.


4
@driis - Immagino che possiamo usare il mantra delle eccezionali linee guida per le eccezioni quando si sceglie tra First e FirstOrDefault. Grazie per la risposta chiara.
Metro Puffo,

5
L'unica cosa che aggiungerei è che se il valore predefinito per il tipo che stai selezionando potrebbe essere un valore valido, ad esempio il tuo risultato potrebbe essere il valore int 0, quindi gestire l'eccezione sembra essere il modo migliore per gestire questo .
PeterBelm,

25
Grattalo, ho trovato un modo molto più bello di farlo, usa: DefaultIfEmpty (-1) .Primo ()
PeterBelm

5
Take non restituisce esattamente un elemento, ma restituisce al massimo un elemento (se si specifica 1, ovviamente). Potrebbe anche restituire 0 elementi, se la sequenza è inizialmente vuota.
SPIRiT_1984,

3
@RoyiNamir, sì nel contesto della domanda in cui il parametro da prendere è 1. Ho anche notato che in parentesi immediatamente dopo quella frase.
driis

272

.Firstgenererà un'eccezione quando non ci sono risultati. .FirstOrDefaultnon lo farà, restituirà semplicemente null (tipi di riferimento) o il valore predefinito del tipo di valore. (ad esempio come 0per un int.) La domanda qui non è quando si desidera il tipo predefinito, ma altro: si è disposti a gestire un'eccezione o gestire un valore predefinito? Poiché le eccezioni dovrebbero essere eccezionali, FirstOrDefaultè preferibile quando non si è sicuri se si otterranno risultati dalla query. Quando logicamente i dati dovrebbero essere presenti, può essere considerata la gestione delle eccezioni.

Skip()e Take()vengono normalmente utilizzati durante l'impostazione del paging nei risultati. (Come mostrare i primi 10 risultati e i successivi 10 nella pagina successiva, ecc.)

Spero che sia di aiuto.


5
@Jeroen - buon punto su casi d'uso migliori per l'utilizzo di Skip / Take.
Metro Puffo,

4
+1 per la spiegazione che .FirstOrDefaultrestituirà null per i tipi di riferimento. Ero confuso su cosa sarebbe un oggetto "predefinito". Questa risposta l'ha chiarito.
Mike Taverne,

115

.First()genererà un'eccezione se non ci sono righe da restituire, mentre .FirstOrDefault()restituirà invece il valore predefinito ( NULLper tutti i tipi di riferimento).

Quindi, se sei preparato e disposto a gestire una possibile eccezione, .First()va bene. Se preferisci comunque verificare il valore di ritorno != null, allora .FirstOrDefault()è la scelta migliore.

Ma credo sia anche una preferenza personale. Usa qualunque cosa abbia più senso per te e si adatti meglio al tuo stile di programmazione.


66

Primo()

  1. Restituisce il primo elemento di una sequenza.
  2. Emette un errore quando non ci sono elementi nel risultato o la sorgente è nulla.
  3. dovresti usarlo, se è previsto più di un elemento e vuoi solo il primo elemento.

FirstOrDefault ()

  1. Restituisce il primo elemento di una sequenza o un valore predefinito se non viene trovato alcun elemento.
  2. Genera un errore Solo se l'origine è nulla.
  3. dovresti usarlo, se è previsto più di un elemento e vuoi solo il primo elemento. Buono anche se il risultato è vuoto.

Abbiamo una tabella UserInfos, che ha alcuni record come mostrato di seguito. Sulla base di questa tabella di seguito ho creato un esempio ...

Tabella UserInfo

Come usare First ()

var result = dc.UserInfos.First(x => x.ID == 1);

Esiste un solo record in cui ID == 1. Dovrebbe restituire questo
ID record : 1 Nome: Manish Cognome: Dubey Email: xyz@xyz.com

var result = dc.UserInfos.First(x => x.FName == "Rahul");   

Esistono più record in cui FName == "Rahul". Il primo record dovrebbe essere restituito.
ID: 7 Nome: Rahul Cognome: Sharma Email: xyz1@xyz.com

var result = dc.UserInfos.First(x => x.ID ==13);

Non ci sono record con ID == 13. Dovrebbe verificarsi un errore.
InvalidOperationException: la sequenza non contiene elementi

Come utilizzare FirstOrDefault ()

var result = dc.UserInfos.FirstOrDefault(x => x.ID == 1);

Esiste un solo record in cui ID == 1. Dovrebbe restituire questo
ID record : 1 Nome: Manish Cognome: Dubey Email: xyz@xyz.com

var result = dc.UserInfos.FirstOrDefault(x => x.FName == "Rahul");

Esistono più record in cui FName == "Rahul". Il primo record dovrebbe essere restituito.
ID: 7 Nome: Rahul Cognome: Sharma Email: xyz1@xyz.com

var result = dc.UserInfos.FirstOrDefault(x => x.ID ==13);

Nessun record con ID == 13. Il valore restituito è null

Spero che ti possa aiutare a capire quando usare First()o FirstOrDefault().


4
A mio avviso, l'affermazione "Dovrebbe verificarsi un errore". sotto il terzo FirstOrDefault () - l'esempio è fuorviante.
Jannik,

Ciao, mi spieghi bene, ma sono un po 'confuso quando si ottengono i dati dal join e quando l'ID non esisteva in una tabella di chiavi esterne in quel momento quale viene utilizzato? Attualmente sto usando First () ma dopo aver letto la tua risposta non ne ho idea.Per favore aiuto
Brijesh Mavani

20

Prima di tutto, Takeè un metodo completamente diverso. Restituisce un IEnumerable<T>e non un singolo T, quindi è fuori.

Tra Firste FirstOrDefault, dovresti usare Firstquando sei sicuro che un elemento esiste e se non lo è, allora c'è un errore.

A proposito, se la sequenza contiene default(T)elementi (ad es. null) E devi distinguere tra essere vuoto e essere il primo elemento null, non puoi usare FirstOrDefault.


2
@Mehrdad - ottimi punti, ri: .First restituisce IEnumerable e quando non usare FirstOrDefault.
Metro Puffo,

15

Primo:

  • Restituisce il primo elemento di una sequenza
  • Genera eccezione: non ci sono elementi nel risultato
  • Utilizzare quando: quando è previsto più di 1 elemento e si desidera solo il primo

FirstOrDefault:

  • Restituisce il primo elemento di una sequenza o un valore predefinito se non viene trovato alcun elemento
  • Genera eccezione: solo se l'origine è nulla
  • Utilizzare quando: quando è previsto più di 1 elemento e si desidera solo il primo. Inoltre va bene che il risultato sia vuoto

Da: http://www.technicaloverload.com/linq-single-vs-singleordefault-vs-first-vs-firstordefault/


10

Un'altra differenza da notare è che se stai eseguendo il debug di un'applicazione in un ambiente di produzione potresti non avere accesso ai numeri di riga, quindi identificare quale particolare .First()istruzione in un metodo ha generato l'eccezione potrebbe essere difficile.

Il messaggio di eccezione non includerà anche le espressioni Lambda che potresti aver usato che renderebbero più difficile il debug di qualsiasi problema.

Ecco perché lo uso sempre FirstOrDefault()anche se so che una voce nulla costituirebbe una situazione eccezionale.

var customer = context.Customers.FirstOrDefault(i => i.Id == customerId);
if (customer == null)
{
   throw new Exception(string.Format("Can't find customer {0}.", customerId));
}

5

Primo()

Quando sai che il risultato contiene più di 1 elemento previsto e dovresti solo il primo elemento della sequenza.

FirstOrDefault ()

FirstOrDefault () è proprio come First () tranne che, se nessun elemento corrisponde alla condizione specificata di quanto restituisca il valore predefinito del tipo sottostante di raccolta generica. Non genera InvalidOperationException se non viene trovato alcun elemento. Ma la raccolta di elementi o sequenze è nulla di quanto non generi un'eccezione.


Ciao, mi spieghi bene, ma sono un po 'confuso quando si ottengono i dati dal join e quando l'ID non esisteva in una tabella di chiavi esterne in quel momento quale viene utilizzato? Attualmente sto usando First () ma dopo aver letto la tua risposta non ne ho idea.Per favore aiuto
Brijesh Mavani

4

Questo tipo di funzione appartiene agli operatori degli elementi. Di seguito sono definiti alcuni operatori di elementi utili.

  1. In primo luogo / FirstOrDefault
  2. Ultima / LastOrDefault
  3. Singolo / SingleOrDefault

Usiamo gli operatori di elementi quando è necessario selezionare un singolo elemento da una sequenza basata su una determinata condizione. Ecco un esempio

  List<int> items = new List<int>() { 8, 5, 2, 4, 2, 6, 9, 2, 10 };

L'operatore First () restituisce il primo elemento di una sequenza dopo aver soddisfatto la condizione. Se non viene trovato alcun elemento, verrà generata un'eccezione.

int result = items.Where (item => item == 2) .First ();

L'operatore FirstOrDefault () restituisce il primo elemento di una sequenza dopo aver soddisfatto la condizione. Se non viene trovato alcun elemento, verrà restituito il valore predefinito di quel tipo.

int result1 = items.Where (item => item == 2) .FirstOrDefault ();


ben spiegato con un esempio di facile comprensione.
Arslan Bhatti,


2
someList.First(); // exception if collection is empty.
someList.FirstOrDefault(); // first item or default(Type)

Quale usare? Dovrebbe essere deciso dalla logica di business e non dalla paura di eccezioni / fallimento del programma.

Ad esempio, se la logica aziendale afferma che non possiamo avere zero transazioni in qualsiasi giorno lavorativo (supponiamo solo). Quindi non dovresti provare a gestire questo scenario con una programmazione intelligente. Userò sempre First () su tale raccolta e lascerò che il programma fallisca se qualcos'altro ha rovinato la logica aziendale.

Codice:

var transactionsOnWorkingDay = GetTransactionOnLatestWorkingDay();
var justNeedOneToProcess = transactionsOnWorkingDay.First(): //Not FirstOrDefault()

Mi piacerebbe vedere altri commenti su questo.


Il valore predefinito per i tipi di riferimento e nullable è null.
dsa

Fallire rapidamente è buono - tuttavia per lo scenario che hai descritto, preferirei vedere prima, farlo fallire, catturare l'eccezione e quindi restituire un errore significativo. Like catch (InvalidOperationException e) {throw new InvalidOperationException ("Impossibile avere zero transazioni in un giorno!", E)}; Ma sì, usare il default per evitare di affrontare un vero problema di logica aziendale è molto male.
Mathieson,

1

Ok, lasciami dare i miei due centesimi. First / Firstordefault sono per quando si utilizza il secondo costruttore. Non spiegherò di cosa si tratta, ma è quando potenzialmente ne useresti sempre uno perché non vuoi causare un'eccezione.

person = tmp.FirstOrDefault(new Func<Person, bool>((p) =>
{
    return string.IsNullOrEmpty(p.Relationship);
}));

Non esattamente. Il primo costruttore è ampiamente utilizzato quando è necessario recuperare solo un elemento o evitare un errore di compilazione quando si assegna il risultato a un valore che non è un array e si è certi che la query restituisca esattamente un risultato. Mentre può sembrare più veloce usare il secondo costruttore piuttosto che usare un altro .Where () (perché pensi che LINQ smetta di valutare gli elementi nella lista dopo aver trovato il primo) si ferma sempre al primo elemento
usr-local-ΕΨΗΕΛΩΝ

0

Altri hanno ben descritto la differenza tra First()e FirstOrDefault(). Voglio fare un ulteriore passo nell'interpretazione della semantica di questi metodi. Secondo me FirstOrDefaultviene abusato molto. Nella maggior parte dei casi, quando si filtrano i dati, ci si aspetterebbe di recuperare una raccolta di elementi corrispondenti alla condizione logica o un singolo elemento univoco dal suo identificatore univoco, ad esempio un utente, un libro, un post, ecc. perché possiamo persino arrivare a dire che FirstOrDefault()è un odore di codice non perché c'è qualcosa che non va, ma perché viene usato troppo spesso. Questo post sul blog esplora l'argomento in dettaglio. IMO il più delle volteSingleOrDefault() è un'alternativa molto migliore, quindi fai attenzione a questo errore e assicurati di utilizzare il metodo più appropriato che rappresenti chiaramente il tuo contratto e le tue aspettative.


-6

linq molti modi per implementare una singola query semplice sulle raccolte, solo scriviamo join in sql, un filtro può essere applicato prima o per ultimo a seconda delle necessità e necessità.

Ecco un esempio in cui possiamo trovare un elemento con un ID in una raccolta. Per aggiungere altro su questo, i metodi First, FirstOrDefaultidealmente, restituirebbero lo stesso quando una raccolta ha almeno un record. Se, tuttavia, una raccolta va bene per essere vuota. quindi Firstrestituirà un'eccezione ma FirstOrDefaultverrà restituito nullo predefinito. Ad esempio, intrestituirà 0. Pertanto, l'utilizzo di tale è anche se si dice che sia una preferenza personale, ma è meglio usarlo FirstOrDefaultper evitare la gestione delle eccezioni. ecco un esempio in cui, passiamo sopra una raccolta di liste di transazioni

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.