2015-08-06 9 views
6

In quali casi viene preferita ciascuna soluzione rispetto all'altra?LINQ Any() e Single() vs. SingleOrDefault() con controllo null

Esempio 1:

if (personList.Any(x => x.Name == "Fox Mulder")) 
{ 
    this.Person = personList.Single(x => x.Name == "Fox Mulder"); 
} 

Esempio 2:

var mulder = personList.SingleOrDefault(x => x.Name == "Fox Mulder"); 

if (mulder != null) 
{ 
    this.Person = mulder; 
} 
+2

I _imagine_ (anche se non ho fatto un punto di riferimento) che 'SingleOrDefault' è più performante di' Any'. Quando si parla di Linq, c'è spesso più di un modo per fare qualcosa. Sceglierei l'opzione che ha più senso per te. –

+1

Chiamando Qualsiasi poi Single accede alla lista persone due volte come SingleOrDefault solo una volta. Per quanto riguarda le prestazioni, probabilmente la seconda opzione sarebbe la migliore (anche se non ho effettuato test su questo!) – Ric

risposta

8

Sia Single e SingleOrDefault sarà enumerare la raccolta oltre il primo risultato corrispondente per verificare che sia esattamente un elemento corrispondenti ai criteri, fermandosi alla prossima partita o alla fine della raccolta. Il primo esempio sarà leggermente più lento, poiché la chiamata Any enumererà una quantità sufficiente della raccolta (possibilmente tutta) per determinare se alcuni elementi soddisfano i criteri, fermandosi alla prima corrispondenza o alla fine della raccolta.

C'è un'altra differenza fondamentale: il primo esempio potrebbe generare un'eccezione. Single restituirà l'elemento corrispondente se esiste esattamente uno e genera un'eccezione in caso contrario. Il controllo con Any non verifica questo; verifica solo che ci sia almeno uno.

Sulla base di questi due motivi (principalmente/soprattutto il secondo motivo), l'approccio SingleOrDefault è preferibile qui.


Quindi, ci sono tre casi qui.

Caso 1: Nessun articolo corrisponde alla condizione di

Opzione 1: .Any enumera l'intero set e restituisce false; .Single non esegue mai.

Opzione 2: .SingleOrDefault enumera l'intero set e restituisce null.

Opzioni essenzialmente equivalenti.

Caso 2: Esattamente un elemento corrisponde la condizione

Opzione 1: Any enumera abbastanza del set di trovare la singola partita (potrebbe essere il primo elemento, potrebbe essere l'intero set). Quindi, Single enumera l'intero set per trovare quell'elemento e confermare che nessun altro corrisponde alla condizione.

L'opzione 2: SingleOrDefault enumera l'intero set, restituisce l'unica corrispondenza.

In questo caso, l'opzione 2 è migliore (esattamente un iterazione, rispetto a (1, 2] iterazioni)

Caso 3: più di un elemento corrisponde la condizione

Opzione 1: Any enumera abbastanza per trovare la prima corrispondenza: il numero Single enumera abbastanza per trovare la seconda corrispondenza, genera un'eccezione

L'opzione 2: SingleOrDefault enumera abbastanza per trovare la seconda corrispondenza, genera un'eccezione.

Entrambi generano eccezioni, ma l'opzione 2 arriva più rapidamente. .

+2

'Single' e' SingleOrDefault' non enumerano l'intera collezione - l'arresto al secondo elemento, se trovato, è sufficiente. –

+2

@Jim Secondo MSDN SingleOrDefault() genera anche un'eccezione se esiste più di un elemento risultante: https://msdn.microsoft.com/en-us/library/vstudio/bb342451(v=vs.100).aspx – user1172282

+0

@JacobKrall grazie, non l'avevo considerato. Ho aggiornato il primo paragrafo per riflettere – yoozer8

0

Estrapolando dal is v as linee guida:

vedi sotto, da Casting vs using the 'as' keyword in the CLR:

// Bad code - checks type twice for no reason 
if (randomObject is TargetType) 
{ 
    TargetType foo = (TargetType) randomObject; 
    // Do something with foo 
} 

Utilizzando Any e Single, sei anche controllare la lista due volte. E la stessa logica sembrerebbe applicarsi: Not only is this checking twice, but it may be checking different things, cioè, in un'applicazione multi-thread l'elenco potrebbe essere diverso tra il controllo e l'assegnazione. In casi estremi, l'elemento trovato con Any potrebbe non esistere più quando viene effettuata la chiamata a Single.

Utilizzando questa logica, vorrei andare a favore dell'esempio due in tutti i casi fino a prova contraria.

0

Opzione 3:

this.Person = personList.FirstOrDefault(x => x.Name == "Fox Mulder"); 

se si utilizza Entity Framework e il nome è la chiave primaria, quindi:

this.person = db.personList.Find("Fox Mulder"); 

Entrambi questi lavoro se this.Person è nullo in arrivo, o se la persona non 'trovato, ci si aspetta che this.Person venga sovrascritto con null. FirstOrDefault è il più veloce poiché si fermerà al primo record che corrisponde invece di iterare attraverso l'intera raccolta, ma non genererà un'eccezione se vengono trovati multipli. La soluzione per l'entity framework è ancora migliore in questo caso perché Find utilizzerà la cache EF, probabilmente senza nemmeno dover colpire l'origine dati.