C# Language
Query LINQ
Ricerca…
introduzione
LINQ è un acronimo che sta per L anguage IN tegrated Q uery. È un concetto che integra un linguaggio di query offrendo un modello coerente per lavorare con i dati attraverso vari tipi di fonti e formati di dati; si utilizzano gli stessi schemi di codifica di base per interrogare e trasformare i dati in documenti XML, database SQL, set di dati ADO.NET, raccolte .NET e qualsiasi altro formato per il quale sia disponibile un provider LINQ.
Sintassi
Sintassi delle query:
- da <intervallo variabile> in <raccolta>
- [da <range variable> in <collection>, ...]
- <filtro, unione, raggruppamento, operatori di aggregazione, ...> <espressione lambda>
- <selezionare o groupBy operator> <formulare il risultato>
Sintassi del metodo:
- Enumerable.Aggregate (func)
- Enumerable.Aggregate (seed, func)
- Enumerable.Aggregate (seed, func, resultSelector)
- Enumerable.All (predicato)
- Enumerable.Any ()
- Enumerable.Any (predicato)
- Enumerable.AsEnumerable ()
- Enumerable.Average ()
- Enumerable.Average (selettore)
- Enumerable.Cast <Risultato> ()
- Enumerable.Concat (secondo)
- Enumerable.Contains (valore)
- Enumerable.Contains (valore, comparatore)
- Enumerable.Count ()
- Enumerable.Count (predicato)
- Enumerable.DefaultIfEmpty ()
- Enumerable.DefaultIfEmpty (defaultValue)
- Enumerable.Distinct ()
- Enumerable.Distinct (di confronto)
- Enumerable.ElementAt (indice)
- Enumerable.ElementAtOrDefault (indice)
- Enumerable.Empty ()
- Enumerable.Except (secondo)
- Enumerable.Except (second, comparer)
- Enumerable.First ()
- Enumerable.First (predicato)
- Enumerable.FirstOrDefault ()
- Enumerable.FirstOrDefault (predicato)
- Enumerable.GroupBy (keySelector)
- Enumerable.GroupBy (keySelector, resultSelector)
- Enumerable.GroupBy (keySelector, elementSelector)
- Enumerable.GroupBy (keySelector, comparatore)
- Enumerable.GroupBy (keySelector, resultSelector, comparatore)
- Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
- Enumerable.GroupBy (keySelector, elementSelector, comparatore)
- Enumerable.GroupBy (keySelector, elementSelector, resultSelector, comparatore)
- Enumerable.Intersect (secondo)
- Enumerable.Intersect (second, comparer)
- Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
- Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, comparatore)
- Enumerable.Last ()
- Enumerable.Last (predicato)
- Enumerable.LastOrDefault ()
- Enumerable.LastOrDefault (predicato)
- Enumerable.LongCount ()
- Enumerable.LongCount (predicato)
- Enumerable.Max ()
- Enumerable.Max (selettore)
- Enumerable.Min ()
- Enumerable.Min (selettore)
- Enumerable.OfType <TResult> ()
- Enumerable.OrderBy (keySelector)
- Enumerable.OrderBy (keySelector, comparatore)
- Enumerable.OrderByDescending (keySelector)
- Enumerable.OrderByDescending (keySelector, comparatore)
- Enumerable.Range (avvia, conta)
- Enumerable.Repeat (element, count)
- Enumerable.Reverse ()
- Enumerable.Select (selettore)
- Enumerable.SelectMany (selettore)
- Enumerable.SelectMany (collectionSelector, resultSelector)
- Enumerable.SequenceEqual (secondo)
- Enumerable.SequenceEqual (secondo, comparatore)
- Enumerable.Single ()
- Enumerable.Single (predicato)
- Enumerable.SingleOrDefault ()
- Enumerable.SingleOrDefault (predicato)
- Enumerable.Skip (conteggio)
- Enumerable.SkipWhile (predicato)
- Enumerable.Sum ()
- Enumerable.Sum (selettore)
- Enumerable.Take (conteggio)
- Enumerable.TakeWhile (predicato)
- orderedEnumerable.ThenBy (keySelector)
- orderedEnumerable.ThenBy (keySelector, comparatore)
- orderedEnumerable.ThenByDescending (keySelector)
- orderedEnumerable.ThenByDescending (keySelector, comparatore)
- Enumerable.ToArray ()
- Enumerable.ToDictionary (keySelector)
- Enumerable.ToDictionary (keySelector, elementSelector)
- Enumerable.ToDictionary (keySelector, comparatore)
- Enumerable.ToDictionary (keySelector, elementSelector, comparatore)
- Enumerable.ToList ()
- Enumerable.ToLookup (keySelector)
- Enumerable.ToLookup (keySelector, elementSelector)
- Enumerable.ToLookup (keySelector, comparatore)
- Enumerable.ToLookup (keySelector, elementSelector, comparatore)
- Enumerable.Union (secondo)
- Enumerable.Union (second, comparer)
- Enumerable.Where (predicato)
- Enumerable.Zip (second, resultSelector)
Osservazioni
Per utilizzare le query LINQ è necessario importare System.Linq
.
La sintassi del metodo è più potente e flessibile, ma la sintassi delle query potrebbe essere più semplice e più familiare. Tutte le query scritte nella sintassi Query sono tradotte nella sintassi funzionale dal compilatore, quindi le prestazioni sono le stesse.
Gli oggetti di query non vengono valutati fino a quando non vengono utilizzati, in modo che possano essere modificati o aggiunti senza penalizzare le prestazioni.
Dove
Restituisce un sottoinsieme di elementi che il predicato specificato è vero per loro.
List<string> trees = new List<string>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };
Sintassi del metodo
// Select all trees with name of length 3
var shortTrees = trees.Where(tree => tree.Length == 3); // Oak, Elm
Sintassi delle query
var shortTrees = from tree in trees
where tree.Length == 3
select tree; // Oak, Elm
Seleziona - Trasforma elementi
Select consente di applicare una trasformazione a ogni elemento in qualsiasi struttura dati che implementa IEnumerable.
Ottenere il primo carattere di ogni stringa nel seguente elenco:
List<String> trees = new List<String>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };
Usando la sintassi normale (lambda)
//The below select stament transforms each element in tree into its first character.
IEnumerable<String> initials = trees.Select(tree => tree.Substring(0, 1));
foreach (String initial in initials) {
System.Console.WriteLine(initial);
}
Produzione:
O
B
B
E
H
M
Utilizzando la sintassi Query LINQ
initials = from tree in trees
select tree.Substring(0, 1);
Metodi di concatenamento
Molte funzioni LINQ funzionano entrambe su un oggetto IEnumerable<TSource>
e restituiscono anche un IEnumerable<TResult>
. I parametri di tipo TSource
e TResult
possono o meno riferirsi allo stesso tipo, a seconda del metodo in questione e delle eventuali funzioni passate ad esso.
Alcuni esempi di questo sono
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector
)
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, int, bool> predicate
)
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
Mentre alcuni metodi di concatenamento possono richiedere l' esecuzione di un intero set prima di andare avanti, LINQ sfrutta l' esecuzione differita usando MSDN return return che crea un Enumerable e un Enumerator dietro le quinte. Il processo di concatenamento in LINQ consiste essenzialmente nella costruzione di un enumerabile (iteratore) per il set originale - che viene rinviato - fino a quando non si materializza enumerando l'enumerabile .
Ciò consente a queste funzioni di essere fluentemente collegate a wiki , in cui una funzione può agire direttamente sul risultato di un'altra. Questo stile di codice può essere utilizzato per eseguire molte operazioni basate su sequenze in una singola istruzione.
Ad esempio, è possibile combinare Select
, Where
e OrderBy
per trasformare, filtrare e ordinare una sequenza in una singola istruzione.
var someNumbers = { 4, 3, 2, 1 };
var processed = someNumbers
.Select(n => n * 2) // Multiply each number by 2
.Where(n => n != 6) // Keep all the results, except for 6
.OrderBy(n => n); // Sort in ascending order
Produzione:
2
4
8
Qualsiasi funzione che estenda e restituisca il tipo IEnumerable<T>
generico può essere utilizzata come clausole concatenate in una singola istruzione. Questo stile di programmazione fluida è potente e dovrebbe essere preso in considerazione quando si creano i propri metodi di estensione .
Intervallo e ripetizione
I metodi statici Range
e Repeat
su Enumerable
possono essere utilizzati per generare sequenze semplici.
Gamma
Enumerable.Range()
genera una sequenza di numeri interi dati un valore iniziale e un conteggio.
// Generate a collection containing the numbers 1-100 ([1, 2, 3, ..., 98, 99, 100])
var range = Enumerable.Range(1,100);
Ripetere
Enumerable.Repeat()
genera una sequenza di elementi ripetuti in base a un elemento e al numero di ripetizioni richieste.
// Generate a collection containing "a", three times (["a","a","a"])
var repeatedValues = Enumerable.Repeat("a", 3);
Salta e prendi
Il metodo Skip restituisce una collezione escludendo un numero di elementi dall'inizio della raccolta di origine. Il numero di articoli esclusi è il numero dato come argomento. Se nella raccolta sono presenti meno elementi di quelli specificati nell'argomento, viene restituita una raccolta vuota.
Il metodo Take restituisce una raccolta contenente un numero di elementi dall'inizio della raccolta di origine. Il numero di elementi inclusi è il numero indicato come argomento. Se nella raccolta sono presenti meno elementi di quelli specificati nell'argomento, la raccolta restituita conterrà gli stessi elementi della raccolta di origine.
var values = new [] { 5, 4, 3, 2, 1 };
var skipTwo = values.Skip(2); // { 3, 2, 1 }
var takeThree = values.Take(3); // { 5, 4, 3 }
var skipOneTakeTwo = values.Skip(1).Take(2); // { 4, 3 }
var takeZero = values.Take(0); // An IEnumerable<int> with 0 items
Skip and Take sono comunemente usati insieme per impaginare i risultati, ad esempio:
IEnumerable<T> GetPage<T>(IEnumerable<T> collection, int pageNumber, int resultsPerPage) {
int startIndex = (pageNumber - 1) * resultsPerPage;
return collection.Skip(startIndex).Take(resultsPerPage);
}
Avviso: LINQ to Entities supporta solo Salta sulle query ordinate . Se si tenta di utilizzare Skip senza ordinare, verrà visualizzato NotSupportedException con il messaggio "Il metodo 'Skip' è supportato solo per l'input ordinato in LINQ alle entità. Il metodo 'OrderBy' deve essere chiamato prima del metodo 'Salta'."
Innanzitutto, FirstOrDefault, Last, LastOrDefault, Single e SingleOrDefault
Tutti e sei i metodi restituiscono un singolo valore del tipo di sequenza e possono essere chiamati con o senza un predicato.
A seconda del numero di elementi che corrispondono al predicate
o, se non viene fornito alcun predicate
, il numero di elementi nella sequenza sorgente, si comportano come segue:
Primo()
- Restituisce il primo elemento di una sequenza o il primo elemento che corrisponde al
predicate
fornito. - Se la sequenza non contiene elementi, viene generata una
InvalidOperationException
con il messaggio: "Sequenza non contiene elementi". - Se la sequenza non contiene elementi corrispondenti al
predicate
fornito, viene generata unaInvalidOperationException
con il messaggio "Sequenza non contiene alcun elemento corrispondente".
Esempio
// Returns "a":
new[] { "a" }.First();
// Returns "a":
new[] { "a", "b" }.First();
// Returns "b":
new[] { "a", "b" }.First(x => x.Equals("b"));
// Returns "ba":
new[] { "ba", "be" }.First(x => x.Contains("b"));
// Throws InvalidOperationException:
new[] { "ca", "ce" }.First(x => x.Contains("b"));
// Throws InvalidOperationException:
new string[0].First();
FirstOrDefault ()
- Restituisce il primo elemento di una sequenza o il primo elemento che corrisponde al
predicate
fornito. - Se la sequenza non contiene elementi o nessun elemento che corrisponde al
predicate
fornito, restituisce il valore predefinito del tipo di sequenza usando ildefault(T)
.
Esempio
// Returns "a":
new[] { "a" }.FirstOrDefault();
// Returns "a":
new[] { "a", "b" }.FirstOrDefault();
// Returns "b":
new[] { "a", "b" }.FirstOrDefault(x => x.Equals("b"));
// Returns "ba":
new[] { "ba", "be" }.FirstOrDefault(x => x.Contains("b"));
// Returns null:
new[] { "ca", "ce" }.FirstOrDefault(x => x.Contains("b"));
// Returns null:
new string[0].FirstOrDefault();
Scorso()
- Restituisce l'ultimo elemento di una sequenza o l'ultimo elemento che corrisponde al
predicate
fornito. - Se la sequenza non contiene elementi, viene lanciata una
InvalidOperationException
con il messaggio "Sequenza non contiene elementi". - Se la sequenza non contiene elementi corrispondenti al
predicate
fornito, viene generata unaInvalidOperationException
con il messaggio "Sequenza non contiene alcun elemento corrispondente".
Esempio
// Returns "a":
new[] { "a" }.Last();
// Returns "b":
new[] { "a", "b" }.Last();
// Returns "a":
new[] { "a", "b" }.Last(x => x.Equals("a"));
// Returns "be":
new[] { "ba", "be" }.Last(x => x.Contains("b"));
// Throws InvalidOperationException:
new[] { "ca", "ce" }.Last(x => x.Contains("b"));
// Throws InvalidOperationException:
new string[0].Last();
LastOrDefault ()
- Restituisce l'ultimo elemento di una sequenza o l'ultimo elemento che corrisponde al
predicate
fornito. - Se la sequenza non contiene elementi o nessun elemento che corrisponde al
predicate
fornito, restituisce il valore predefinito del tipo di sequenza usando ildefault(T)
.
Esempio
// Returns "a":
new[] { "a" }.LastOrDefault();
// Returns "b":
new[] { "a", "b" }.LastOrDefault();
// Returns "a":
new[] { "a", "b" }.LastOrDefault(x => x.Equals("a"));
// Returns "be":
new[] { "ba", "be" }.LastOrDefault(x => x.Contains("b"));
// Returns null:
new[] { "ca", "ce" }.LastOrDefault(x => x.Contains("b"));
// Returns null:
new string[0].LastOrDefault();
Singolo ()
- Se la sequenza contiene esattamente un elemento, o esattamente un elemento che corrisponde al
predicate
fornito, tale elemento viene restituito. - Se la sequenza non contiene elementi o nessun elemento corrispondente al
predicate
fornito, viene generata unaInvalidOperationException
con il messaggio "Sequenza non contiene elementi". - Se la sequenza contiene più di un elemento o più di un elemento che corrisponde al
predicate
fornito, viene generata unaInvalidOperationException
con il messaggio "Sequenza contiene più di un elemento". - Nota: per valutare se la sequenza contiene esattamente un elemento, al massimo due elementi devono essere enumerati.
Esempio
// Returns "a":
new[] { "a" }.Single();
// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "b" }.Single();
// Returns "b":
new[] { "a", "b" }.Single(x => x.Equals("b"));
// Throws InvalidOperationException:
new[] { "a", "b" }.Single(x => x.Equals("c"));
// Throws InvalidOperationException:
new string[0].Single();
// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "a" }.Single();
SingleOrDefault ()
- Se la sequenza contiene esattamente un elemento, o esattamente un elemento che corrisponde al
predicate
fornito, tale elemento viene restituito. - Se la sequenza non contiene elementi o nessun elemento corrispondente al
predicate
fornito, viene restituito ildefault(T)
. - Se la sequenza contiene più di un elemento o più di un elemento che corrisponde al
predicate
fornito, viene generata unaInvalidOperationException
con il messaggio "Sequenza contiene più di un elemento". - Se la sequenza non contiene elementi corrispondenti al
predicate
fornito, restituisce il valore predefinito del tipo di sequenza usando ildefault(T)
. - Nota: per valutare se la sequenza contiene esattamente un elemento, al massimo due elementi devono essere enumerati.
Esempio
// Returns "a":
new[] { "a" }.SingleOrDefault();
// returns "a"
new[] { "a", "b" }.SingleOrDefault(x => x == "a");
// Returns null:
new[] { "a", "b" }.SingleOrDefault(x => x == "c");
// Throws InvalidOperationException:
new[] { "a", "a" }.SingleOrDefault(x => x == "a");
// Throws InvalidOperationException:
new[] { "a", "b" }.SingleOrDefault();
// Returns null:
new string[0].SingleOrDefault();
raccomandazioni
Sebbene sia possibile utilizzare
FirstOrDefault
,LastOrDefault
oSingleOrDefault
per verificare se una sequenza contiene elementi,Any
oCount
sono più affidabili. Questo perché un valore di ritorno didefault(T)
da uno di questi tre metodi non dimostra che la sequenza sia vuota, poiché il valore del primo / ultimo / singolo elemento della sequenza potrebbe essere ugualmentedefault(T)
Decidi quali metodi si adattano di più al tuo codice. Ad esempio, usa
Single
solo se devi assicurarti che ci sia un singolo oggetto nella collezione che corrisponde al tuo predicato, altrimenti usaFirst
; comeSingle
lancia un'eccezione se la sequenza ha più di un elemento corrispondente. Questo ovviamente vale anche per le controparti "* OrDefault".Riguardo all'efficienza: sebbene sia spesso opportuno assicurarsi che vi sia un solo elemento (
Single
) o, o solo uno o zero (SingleOrDefault
), restituiti da una query, entrambi questi metodi richiedono più, e spesso l'intero, della raccolta da esaminare per assicurarsi che non ci sia una seconda corrispondenza alla domanda. Questo è diverso dal comportamento di, ad esempio, ilFirst
metodo, che può essere soddisfatto dopo aver trovato la prima corrispondenza.
tranne
Il metodo Except restituisce l'insieme di elementi contenuti nella prima raccolta ma non contenuti nella seconda. Il valore predefinito di IEqualityComparer
viene utilizzato per confrontare gli elementi all'interno dei due set. C'è un sovraccarico che accetta un IEqualityComparer
come argomento.
Esempio:
int[] first = { 1, 2, 3, 4 };
int[] second = { 0, 2, 3, 5 };
IEnumerable<int> inFirstButNotInSecond = first.Except(second);
// inFirstButNotInSecond = { 1, 4 }
Produzione:
1
4
In questo caso. .Except(second)
esclude gli elementi contenuti nell'array second
, ovvero 2 e 3 (0 e 5 non sono contenuti nel first
array e vengono saltati).
Nota che Except
implica Distinct
(cioè rimuove gli elementi ripetuti). Per esempio:
int[] third = { 1, 1, 1, 2, 3, 4 };
IEnumerable<int> inThirdButNotInSecond = third.Except(second);
// inThirdButNotInSecond = { 1, 4 }
Produzione:
1
4
In questo caso, gli elementi 1 e 4 vengono restituiti una sola volta.
Implementando IEquatable
o fornendo la funzione a IEqualityComparer
consentirà di utilizzare un metodo diverso per confrontare gli elementi. Si noti che il metodo GetHashCode
deve essere sovrascritto in modo che restituisca un codice hash identico per l' object
identico in base all'implementazione IEquatable
.
Esempio con IEquatable:
class Holiday : IEquatable<Holiday>
{
public string Name { get; set; }
public bool Equals(Holiday other)
{
return Name == other.Name;
}
// GetHashCode must return true whenever Equals returns true.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
return Name?.GetHashCode() ?? 0;
}
}
public class Program
{
public static void Main()
{
List<Holiday> holidayDifference = new List<Holiday>();
List<Holiday> remoteHolidays = new List<Holiday>
{
new Holiday { Name = "Xmas" },
new Holiday { Name = "Hanukkah" },
new Holiday { Name = "Ramadan" }
};
List<Holiday> localHolidays = new List<Holiday>
{
new Holiday { Name = "Xmas" },
new Holiday { Name = "Ramadan" }
};
holidayDifference = remoteHolidays
.Except(localHolidays)
.ToList();
holidayDifference.ForEach(x => Console.WriteLine(x.Name));
}
}
Produzione:
Hanukkah
SelectMany: appiattimento di una sequenza di sequenze
var sequenceOfSequences = new [] { new [] { 1, 2, 3 }, new [] { 4, 5 }, new [] { 6 } };
var sequence = sequenceOfSequences.SelectMany(x => x);
// returns { 1, 2, 3, 4, 5, 6 }
Utilizzare SelectMany()
se si dispone, o si sta creando una sequenza di sequenze, ma si desidera il risultato come una lunga sequenza.
In LINQ Query Sintassi:
var sequence = from subSequence in sequenceOfSequences
from item in subSequence
select item;
Se si dispone di una raccolta di raccolte e si desidera poter lavorare contemporaneamente ai dati dalla raccolta padre e figlio, è anche possibile con SelectMany
.
Definiamo classi semplici
public class BlogPost
{
public int Id { get; set; }
public string Content { get; set; }
public List<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string Content { get; set; }
}
Supponiamo di avere la seguente collezione.
List<BlogPost> posts = new List<BlogPost>()
{
new BlogPost()
{
Id = 1,
Comments = new List<Comment>()
{
new Comment()
{
Id = 1,
Content = "It's really great!",
},
new Comment()
{
Id = 2,
Content = "Cool post!"
}
}
},
new BlogPost()
{
Id = 2,
Comments = new List<Comment>()
{
new Comment()
{
Id = 3,
Content = "I don't think you're right",
},
new Comment()
{
Id = 4,
Content = "This post is a complete nonsense"
}
}
}
};
Ora vogliamo selezionare i commenti Content
con Id
di BlogPost
associata a questo commento. Per fare ciò, possiamo usare il sovraccarico SelectMany
appropriato.
var commentsWithIds = posts.SelectMany(p => p.Comments, (post, comment) => new { PostId = post.Id, CommentContent = comment.Content });
I nostri commentsWithIds
assomiglia a questo
{
PostId = 1,
CommentContent = "It's really great!"
},
{
PostId = 1,
CommentContent = "Cool post!"
},
{
PostId = 2,
CommentContent = "I don't think you're right"
},
{
PostId = 2,
CommentContent = "This post is a complete nonsense"
}
SelectMany
Il metodo SelectMany linq 'appiattisce' un oggetto IEnumerable<IEnumerable<T>>
in IEnumerable<T>
. Tutti gli elementi T all'interno delle istanze IEnumerable
contenute nella sorgente IEnumerable
verranno combinati in un singolo oggetto IEnumerable
.
var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }
Se si utilizza una funzione di selezione che trasforma gli elementi di input in sequenze, il risultato saranno gli elementi di tali sequenze restituite una alla volta.
Si noti che, diversamente da Select()
, il numero di elementi nell'output non deve necessariamente essere lo stesso dell'input.
Più esempio del mondo reale
class School
{
public Student[] Students { get; set; }
}
class Student
{
public string Name { get; set; }
}
var schools = new [] {
new School(){ Students = new [] { new Student { Name="Bob"}, new Student { Name="Jack"} }},
new School(){ Students = new [] { new Student { Name="Jim"}, new Student { Name="John"} }}
};
var allStudents = schools.SelectMany(s=> s.Students);
foreach(var student in allStudents)
{
Console.WriteLine(student.Name);
}
Produzione:
peso
Jack
Jim
John
Tutti
All
è usato per controllare, se tutti gli elementi di una collezione corrispondono a una condizione o meno.
vedi anche:. Any
1. Parametro vuoto
Tutto : non è consentito l'uso con parametro vuoto.
2. Espressione lambda come parametro
Tutto : restituisce true
se tutti gli elementi della raccolta soddisfano l'espressione lambda e false
altrimenti:
var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.All(i => i < 10); // true
bool result = numbers.All(i => i >= 3); // false
3. Raccolta vuota
Tutto : restituisce true
se la raccolta è vuota e viene fornita un'espressione lambda:
var numbers = new List<int>();
bool result = numbers.All(i => i >= 0); // true
Nota: All
interromperanno l'iterazione della raccolta non appena trova un elemento che non corrisponde alla condizione. Ciò significa che la raccolta non sarà necessariamente completamente elencata; sarà elencato solo abbastanza lontano da trovare il primo elemento che non corrisponde alla condizione.
Query raccolta per tipo / cast elementi da digitare
interface IFoo { }
class Foo : IFoo { }
class Bar : IFoo { }
var item0 = new Foo();
var item1 = new Foo();
var item2 = new Bar();
var item3 = new Bar();
var collection = new IFoo[] { item0, item1, item2, item3 };
Utilizzando OfType
var foos = collection.OfType<Foo>(); // result: IEnumerable<Foo> with item0 and item1
var bars = collection.OfType<Bar>(); // result: IEnumerable<Bar> item item2 and item3
var foosAndBars = collection.OfType<IFoo>(); // result: IEnumerable<IFoo> with all four items
Usando Where
var foos = collection.Where(item => item is Foo); // result: IEnumerable<IFoo> with item0 and item1
var bars = collection.Where(item => item is Bar); // result: IEnumerable<IFoo> with item2 and item3
Utilizzando Cast
var bars = collection.Cast<Bar>(); // throws InvalidCastException on the 1st item
var foos = collection.Cast<Foo>(); // throws InvalidCastException on the 3rd item
var foosAndBars = collection.Cast<IFoo>(); // OK
Unione
Unisce due raccolte per creare una raccolta distinta utilizzando il comparatore di uguaglianza predefinito
int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 2, 3, 4, 5 };
var allElement = numbers1.Union(numbers2); // AllElement now contains 1,2,3,4,5
SI UNISCE
I join vengono utilizzati per combinare diversi elenchi o tabelle contenenti i dati tramite una chiave comune.
Come in SQL, i seguenti tipi di join sono supportati in LINQ:
Giunzioni esterne interne, a sinistra, a destra, a croce e complete .
I seguenti due elenchi sono utilizzati negli esempi seguenti:
var first = new List<string>(){ "a","b","c"}; // Left data
var second = new List<string>(){ "a", "c", "d"}; // Right data
(Interno) Unisciti
var result = from f in first
join s in second on f equals s
select new { f, s };
var result = first.Join(second,
f => f,
s => s,
(f, s) => new { f, s });
// Result: {"a","a"}
// {"c","c"}
Giuntura esterna sinistra
var leftOuterJoin = from f in first
join s in second on f equals s into temp
from t in temp.DefaultIfEmpty()
select new { First = f, Second = t};
// Or can also do:
var leftOuterJoin = from f in first
from s in second.Where(x => x == f).DefaultIfEmpty()
select new { First = f, Second = s};
// Result: {"a","a"}
// {"b", null}
// {"c","c"}
// Left outer join method syntax
var leftOuterJoinFluentSyntax = first.GroupJoin(second,
f => f,
s => s,
(f, s) => new { First = f, Second = s })
.SelectMany(temp => temp.Second.DefaultIfEmpty(),
(f, s) => new { First = f.First, Second = s });
Giusto outer join
var rightOuterJoin = from s in second
join f in first on s equals f into temp
from t in temp.DefaultIfEmpty()
select new {First=t,Second=s};
// Result: {"a","a"}
// {"c","c"}
// {null,"d"}
Cross Join
var CrossJoin = from f in first
from s in second
select new { f, s };
// Result: {"a","a"}
// {"a","c"}
// {"a","d"}
// {"b","a"}
// {"b","c"}
// {"b","d"}
// {"c","a"}
// {"c","c"}
// {"c","d"}
Full Outer Join
var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);
// Result: {"a","a"}
// {"b", null}
// {"c","c"}
// {null,"d"}
Esempio pratico
Gli esempi sopra hanno una struttura dati semplice in modo che tu possa concentrarti sulla comprensione dei diversi LINQ che si uniscono tecnicamente, ma nel mondo reale avresti tabelle con colonne a cui devi unirti.
Nell'esempio seguente, vi è una sola Region
classe utilizzata, in realtà si unirebbero due o più tabelle diverse che contengono la stessa chiave (in questo esempio, il first
e il second
vengono uniti tramite l' ID
chiave comune).
Esempio: considerare la seguente struttura di dati:
public class Region
{
public Int32 ID;
public string RegionDescription;
public Region(Int32 pRegionID, string pRegionDescription=null)
{
ID = pRegionID; RegionDescription = pRegionDescription;
}
}
Ora prepara i dati (cioè compila con i dati):
// Left data
var first = new List<Region>()
{ new Region(1), new Region(3), new Region(4) };
// Right data
var second = new List<Region>()
{
new Region(1, "Eastern"), new Region(2, "Western"),
new Region(3, "Northern"), new Region(4, "Southern")
};
Puoi vedere che in questo esempio first
non contiene alcuna descrizione di regione, quindi vuoi unirti a loro dal second
. Quindi l'unione interna sarebbe simile a:
// do the inner join
var result = from f in first
join s in second on f.ID equals s.ID
select new { f.ID, s.RegionDescription };
// Result: {1,"Eastern"}
// {3, Northern}
// {4,"Southern"}
Questo risultato ha creato oggetti anonimi al volo, il che va bene, ma abbiamo già creato una classe appropriata, quindi possiamo specificarla: invece di select new { f.ID, s.RegionDescription };
possiamo dire select new Region(f.ID, s.RegionDescription);
, che restituirà gli stessi dati ma creerà oggetti di tipo Region
, che manterranno la compatibilità con gli altri oggetti.
distinto
Restituisce valori univoci da un oggetto IEnumerable
. L'univocità viene determinata utilizzando il comparatore di uguaglianza predefinito.
int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };
var distinct = array.Distinct();
// distinct = { 1, 2, 3, 4, 5 }
Per confrontare un tipo di dati personalizzato, è necessario implementare l' IEquatable<T>
e fornire i metodi GetHashCode
ed Equals
per il tipo. Oppure il comparatore di uguaglianza può essere sovrascritto:
class SSNEqualityComparer : IEqualityComparer<Person> {
public bool Equals(Person a, Person b) => return a.SSN == b.SSN;
public int GetHashCode(Person p) => p.SSN;
}
List<Person> people;
distinct = people.Distinct(SSNEqualityComparer);
Raggruppa uno o più campi
Supponiamo che abbiamo qualche modello di film:
public class Film {
public string Title { get; set; }
public string Category { get; set; }
public int Year { get; set; }
}
Raggruppa per categoria proprietà:
foreach (var grp in films.GroupBy(f => f.Category)) {
var groupCategory = grp.Key;
var numberOfFilmsInCategory = grp.Count();
}
Raggruppa per categoria e anno:
foreach (var grp in films.GroupBy(f => new { Category = f.Category, Year = f.Year })) {
var groupCategory = grp.Key.Category;
var groupYear = grp.Key.Year;
var numberOfFilmsInCategory = grp.Count();
}
Utilizzo di Range con vari metodi Linq
È possibile utilizzare la classe Enumerable insieme alle query Linq per convertire i loop in Linq one liners.
Seleziona Esempio
Opposto a questo:
var asciiCharacters = new List<char>();
for (var x = 0; x < 256; x++)
{
asciiCharacters.Add((char)x);
}
Puoi farlo:
var asciiCharacters = Enumerable.Range(0, 256).Select(a => (char) a);
Dove esempio
In questo esempio, verranno generati 100 numeri e anche quelli verranno estratti
var evenNumbers = Enumerable.Range(1, 100).Where(a => a % 2 == 0);
Ordinamento delle query - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()
string[] names= { "mark", "steve", "adam" };
Ascendente:
Sintassi delle query
var sortedNames =
from name in names
orderby name
select name;
Sintassi del metodo
var sortedNames = names.OrderBy(name => name);
sortedNames contiene i nomi nell'ordine seguente: "adam", "mark", "steve"
Discendente:
Sintassi delle query
var sortedNames =
from name in names
orderby name descending
select name;
Sintassi del metodo
var sortedNames = names.OrderByDescending(name => name);
sortedNames contiene i nomi nell'ordine seguente: "steve", "mark", "adam"
Ordina per più campi
Person[] people =
{
new Person { FirstName = "Steve", LastName = "Collins", Age = 30},
new Person { FirstName = "Phil" , LastName = "Collins", Age = 28},
new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 29},
new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 15}
};
Sintassi delle query
var sortedPeople = from person in people
orderby person.LastName, person.FirstName, person.Age descending
select person;
Sintassi del metodo
sortedPeople = people.OrderBy(person => person.LastName)
.ThenBy(person => person.FirstName)
.ThenByDescending(person => person.Age);
Risultato
1. Adam Ackerman 29
2. Adam Ackerman 15
3. Phil Collins 28
4. Steve Collins 30
Nozioni di base
LINQ è in gran parte utile per l'interrogazione di collezioni (o matrici).
Ad esempio, dati i seguenti dati di esempio:
var classroom = new Classroom
{
new Student { Name = "Alice", Grade = 97, HasSnack = true },
new Student { Name = "Bob", Grade = 82, HasSnack = false },
new Student { Name = "Jimmy", Grade = 71, HasSnack = true },
new Student { Name = "Greg", Grade = 90, HasSnack = false },
new Student { Name = "Joe", Grade = 59, HasSnack = false }
}
Possiamo "interrogare" su questi dati usando la sintassi LINQ. Ad esempio, per recuperare tutti gli studenti che hanno uno spuntino oggi:
var studentsWithSnacks = from s in classroom.Students
where s.HasSnack
select s;
Oppure, per recuperare studenti con un punteggio pari o superiore a 90, e restituire solo i loro nomi, non l'oggetto Student
completo:
var topStudentNames = from s in classroom.Students
where s.Grade >= 90
select s.Name;
La funzione LINQ è composta da due sintassi che svolgono le stesse funzioni, hanno prestazioni quasi identiche, ma sono scritte in modo molto diverso. La sintassi dell'esempio precedente è chiamata sintassi della query . L'esempio seguente, tuttavia, illustra la sintassi del metodo . Gli stessi dati verranno restituiti come nell'esempio sopra, ma il modo in cui la query è scritta è diverso.
var topStudentNames = classroom.Students
.Where(s => s.Grade >= 90)
.Select(s => s.Name);
Raggruppa per
GroupBy è un modo semplice per ordinare una raccolta di oggetti IEnumerable<T>
in gruppi distinti.
Semplice esempio
In questo primo esempio, ci ritroviamo con due gruppi, elementi pari e dispari.
List<int> iList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var grouped = iList.GroupBy(x => x % 2 == 0);
//Groups iList into odd [13579] and even[2468] items
foreach(var group in grouped)
{
foreach (int item in group)
{
Console.Write(item); // 135792468 (first odd then even)
}
}
Esempio più complesso
Prendiamo come esempio un elenco di persone per età. Per prima cosa creeremo un oggetto Persona che ha due proprietà, Nome ed Età.
public class Person
{
public int Age {get; set;}
public string Name {get; set;}
}
Quindi creiamo la nostra lista di esempi di persone con vari nomi ed età.
List<Person> people = new List<Person>();
people.Add(new Person{Age = 20, Name = "Mouse"});
people.Add(new Person{Age = 30, Name = "Neo"});
people.Add(new Person{Age = 40, Name = "Morpheus"});
people.Add(new Person{Age = 30, Name = "Trinity"});
people.Add(new Person{Age = 40, Name = "Dozer"});
people.Add(new Person{Age = 40, Name = "Smith"});
Quindi creiamo una query LINQ per raggruppare il nostro elenco di persone per età.
var query = people.GroupBy(x => x.Age);
In questo modo, possiamo vedere l'età per ogni gruppo e avere un elenco di ogni persona nel gruppo.
foreach(var result in query)
{
Console.WriteLine(result.Key);
foreach(var person in result)
Console.WriteLine(person.Name);
}
Ciò risulta nell'output seguente:
20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith
Puoi giocare con la demo live su .NET Fiddle
Qualunque
Any
viene usato per verificare se qualche elemento di una collezione corrisponde a una condizione o meno.
vedi anche: .Tutte , ne e FirstOrDefault: best practice
1. Parametro vuoto
Qualsiasi : restituisce true
se la raccolta ha elementi e false
se la raccolta è vuota:
var numbers = new List<int>();
bool result = numbers.Any(); // false
var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.Any(); //true
2. Espressione lambda come parametro
Qualsiasi : restituisce true
se la raccolta ha uno o più elementi che soddisfano la condizione nell'espressione lambda:
var arrayOfStrings = new string[] { "a", "b", "c" };
arrayOfStrings.Any(item => item == "a"); // true
arrayOfStrings.Any(item => item == "d"); // false
3. Raccolta vuota
Qualsiasi : restituisce false
se la raccolta è vuota e viene fornita un'espressione lambda:
var numbers = new List<int>();
bool result = numbers.Any(i => i >= 0); // false
Nota: Any
interromperà l'iterazione della raccolta non appena trova un elemento corrispondente alla condizione. Ciò significa che la raccolta non sarà necessariamente completamente elencata; verrà elencato solo quanto basta per trovare il primo elemento corrispondente alla condizione.
ToDictionary
Il metodo LINQ ToDictionary()
può essere utilizzato per generare un insieme di Dictionary<TKey, TElement>
basato su una sorgente I Dictionary<TKey, TElement>
IEnumerable<T>
.
IEnumerable<User> users = GetUsers();
Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);
In questo esempio, il singolo argomento passato a ToDictionary
è di tipo Func<TSource, TKey>
, che restituisce la chiave per ciascun elemento.
Questo è un modo conciso per eseguire la seguente operazione:
Dictionary<int, User> usersById = new Dictionary<int User>();
foreach (User u in users)
{
usersById.Add(u.Id, u);
}
È anche possibile passare un secondo parametro al metodo ToDictionary
, che è di tipo Func<TSource, TElement>
e restituisce il Value
da aggiungere per ogni voce.
IEnumerable<User> users = GetUsers();
Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);
È anche possibile specificare IComparer
che viene utilizzato per confrontare i valori chiave. Questo può essere utile quando la chiave è una stringa e vuoi che corrisponda a maiuscole e minuscole.
IEnumerable<User> users = GetUsers();
Dictionary<string, User> usersByCaseInsenstiveName = users.ToDictionary(x => x.Name, StringComparer.InvariantCultureIgnoreCase);
var user1 = usersByCaseInsenstiveName["john"];
var user2 = usersByCaseInsenstiveName["JOHN"];
user1 == user2; // Returns true
Nota: il metodo ToDictionary
richiede che tutte le chiavi siano univoche, non ci devono essere chiavi duplicate. Se ci sono, allora viene generata un'eccezione: ArgumentException: An item with the same key has already been added.
Se hai uno scenario in cui sai che avrai più elementi con la stessa chiave, allora preferisci utilizzare ToLookup
.
Aggregato
Aggregate
Applica una funzione di accumulatore su una sequenza.
int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
- Al primo passo
prevSum = 1
- Al secondo
prevSum = prevSum(at the first step) + 2
- Al passo i-esimo
prevSum = prevSum(at the (i-1) step) + i-th element of the array
passoprevSum = prevSum(at the (i-1) step) + i-th element of the array
string[] stringList = { "Hello", "World", "!" };
string joinedString = stringList.Aggregate((prev, current) => prev + " " + current);
// joinedString = "Hello World !"
Un secondo overload di Aggregate
riceve anche un parametro seed
che è il valore dell'accumulatore iniziale. Questo può essere usato per calcolare più condizioni su una collezione senza iterarlo più di una volta.
List<int> items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
Per la raccolta di items
che vogliamo calcolare
- Il totale
.Count
- La quantità di numeri pari
- Colleziona ogni singolo oggetto
Usando Aggregate
può essere fatto in questo modo:
var result = items.Aggregate(new { Total = 0, Even = 0, FourthItems = new List<int>() },
(accumelative,item) =>
new {
Total = accumelative.Total + 1,
Even = accumelative.Even + (item % 2 == 0 ? 1 : 0),
FourthItems = (accumelative.Total + 1)%4 == 0 ?
new List<int>(accumelative.FourthItems) { item } :
accumelative.FourthItems
});
// Result:
// Total = 12
// Even = 6
// FourthItems = [4, 8, 12]
Si noti che l'utilizzo di un tipo anonimo come seed deve istanziare un nuovo oggetto ogni elemento perché le proprietà sono di sola lettura. Usando una classe personalizzata si può semplicemente assegnare l'informazione e non è necessario alcun new
(solo quando si fornisce il parametro iniziale del seed
Definizione di una variabile all'interno di una query Linq (let keyword)
Per definire una variabile all'interno di un'espressione linq, è possibile utilizzare la parola chiave let . Questo di solito è fatto per archiviare i risultati di sottoquery intermedie, ad esempio:
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var aboveAverages = from number in numbers
let average = numbers.Average()
let nSquared = Math.Pow(number,2)
where nSquared > average
select number;
Console.WriteLine("The average of the numbers is {0}.", numbers.Average());
foreach (int n in aboveAverages)
{
Console.WriteLine("Query result includes number {0} with square of {1}.", n, Math.Pow(n,2));
}
Produzione:
La media dei numeri è 4.5.
Il risultato della query include il numero 3 con il quadrato di 9.
Il risultato della query include il numero 4 con il quadrato di 16.
Il risultato della query include il numero 5 con il quadrato di 25.
Il risultato della query include il numero 6 con il quadrato di 36.
Il risultato della query include il numero 7 con il quadrato di 49.
Il risultato della query include il numero 8 con un quadrato di 64.
Il risultato della query include il numero 9 con il quadrato di 81.
SkipWhile
SkipWhile()
viene utilizzato per escludere elementi fino alla prima non corrispondenza (questo potrebbe essere controintuitivo per la maggior parte)
int[] list = { 42, 42, 6, 6, 6, 42 };
var result = list.SkipWhile(i => i == 42);
// Result: 6, 6, 6, 42
DefaultIfEmpty
DefaultIfEmpty viene utilizzato per restituire un elemento predefinito se la sequenza non contiene elementi. Questo elemento può essere il valore predefinito del tipo o un'istanza definita dall'utente di quel tipo. Esempio:
var chars = new List<string>() { "a", "b", "c", "d" };
chars.DefaultIfEmpty("N/A").FirstOrDefault(); // returns "a";
chars.Where(str => str.Length > 1)
.DefaultIfEmpty("N/A").FirstOrDefault(); // return "N/A"
chars.Where(str => str.Length > 1)
.DefaultIfEmpty().First(); // returns null;
Utilizzo in unione sinistra :
Con DefaultIfEmpty
il tradizionale Linq Join può restituire un oggetto predefinito se non è stata trovata alcuna corrispondenza. Così agendo come un join sinistro di SQL. Esempio:
var leftSequence = new List<int>() { 99, 100, 5, 20, 102, 105 };
var rightSequence = new List<char>() { 'a', 'b', 'c', 'i', 'd' };
var numbersAsChars = from l in leftSequence
join r in rightSequence
on l equals (int)r into leftJoin
from result in leftJoin.DefaultIfEmpty('?')
select new
{
Number = l,
Character = result
};
foreach(var item in numbersAsChars)
{
Console.WriteLine("Num = {0} ** Char = {1}", item.Number, item.Character);
}
ouput:
Num = 99 Char = c
Num = 100 Char = d
Num = 5 Char = ?
Num = 20 Char = ?
Num = 102 Char = ?
Num = 105 Char = i
Nel caso in cui venga utilizzato un DefaultIfEmpty
(senza specificare un valore predefinito) e ciò comporterà l'assenza di elementi corrispondenti nella sequenza corretta, è necessario assicurarsi che l'oggetto non sia null
prima di accedere alle sue proprietà. Altrimenti risulterà in una NullReferenceException
. Esempio:
var leftSequence = new List<int> { 1, 2, 5 };
var rightSequence = new List<dynamic>()
{
new { Value = 1 },
new { Value = 2 },
new { Value = 3 },
new { Value = 4 },
};
var numbersAsChars = (from l in leftSequence
join r in rightSequence
on l equals r.Value into leftJoin
from result in leftJoin.DefaultIfEmpty()
select new
{
Left = l,
// 5 will not have a matching object in the right so result
// will be equal to null.
// To avoid an error use:
// - C# 6.0 or above - ?.
// - Under - result == null ? 0 : result.Value
Right = result?.Value
}).ToList();
SequenceEqual
SequenceEqual
viene utilizzato per confrontare due sequenze I IEnumerable<T>
Enumerabili l'una con l'altra.
int[] a = new int[] {1, 2, 3};
int[] b = new int[] {1, 2, 3};
int[] c = new int[] {1, 3, 2};
bool returnsTrue = a.SequenceEqual(b);
bool returnsFalse = a.SequenceEqual(c);
Count e LongCount
Count
restituisce il numero di elementi in un oggetto IEnumerable<T>
. Count
espone anche un parametro di predicato opzionale che consente di filtrare gli elementi che si desidera contare.
int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };
int n = array.Count(); // returns the number of elements in the array
int x = array.Count(i => i > 2); // returns the number of elements in the array greater than 2
LongCount
funziona allo stesso modo di Count
ma ha un tipo restituito di long
e viene utilizzato per il conteggio IEnumerable<T>
sequenze IEnumerable<T>
che sono più lunghe di int.MaxValue
int[] array = GetLargeArray();
long n = array.LongCount(); // returns the number of elements in the array
long x = array.LongCount(i => i > 100); // returns the number of elements in the array greater than 100
Costruzione incrementale di una query
Poiché LINQ utilizza l' esecuzione posticipata , possiamo avere un oggetto query che in realtà non contiene i valori, ma restituirà i valori quando valutato. Possiamo quindi creare dinamicamente la query in base al nostro flusso di controllo e valutarla una volta che abbiamo finito:
IEnumerable<VehicleModel> BuildQuery(int vehicleType, SearchModel search, int start = 1, int count = -1) {
IEnumerable<VehicleModel> query = _entities.Vehicles
.Where(x => x.Active && x.Type == vehicleType)
.Select(x => new VehicleModel {
Id = v.Id,
Year = v.Year,
Class = v.Class,
Make = v.Make,
Model = v.Model,
Cylinders = v.Cylinders ?? 0
});
Possiamo applicare condizionatamente filtri:
if (!search.Years.Contains("all", StringComparer.OrdinalIgnoreCase))
query = query.Where(v => search.Years.Contains(v.Year));
if (!search.Makes.Contains("all", StringComparer.OrdinalIgnoreCase)) {
query = query.Where(v => search.Makes.Contains(v.Make));
}
if (!search.Models.Contains("all", StringComparer.OrdinalIgnoreCase)) {
query = query.Where(v => search.Models.Contains(v.Model));
}
if (!search.Cylinders.Equals("all", StringComparer.OrdinalIgnoreCase)) {
decimal minCylinders = 0;
decimal maxCylinders = 0;
switch (search.Cylinders) {
case "2-4":
maxCylinders = 4;
break;
case "5-6":
minCylinders = 5;
maxCylinders = 6;
break;
case "8":
minCylinders = 8;
maxCylinders = 8;
break;
case "10+":
minCylinders = 10;
break;
}
if (minCylinders > 0) {
query = query.Where(v => v.Cylinders >= minCylinders);
}
if (maxCylinders > 0) {
query = query.Where(v => v.Cylinders <= maxCylinders);
}
}
Possiamo aggiungere un ordinamento alla query in base a una condizione:
switch (search.SortingColumn.ToLower()) {
case "make_model":
query = query.OrderBy(v => v.Make).ThenBy(v => v.Model);
break;
case "year":
query = query.OrderBy(v => v.Year);
break;
case "engine_size":
query = query.OrderBy(v => v.EngineSize).ThenBy(v => v.Cylinders);
break;
default:
query = query.OrderBy(v => v.Year); //The default sorting.
}
La nostra query può essere definita per iniziare da un punto specifico:
query = query.Skip(start - 1);
e definito per restituire un numero specifico di record:
if (count > -1) {
query = query.Take(count);
}
return query;
}
Una volta ottenuto l'oggetto query, possiamo valutare i risultati con un ciclo foreach
o uno dei metodi LINQ che restituisce un insieme di valori, come ToList
o ToArray
:
SearchModel sm;
// populate the search model here
// ...
List<VehicleModel> list = BuildQuery(5, sm).ToList();
Cerniera lampo
Il metodo di estensione Zip
agisce su due raccolte. Associa ogni elemento delle due serie in base alla posizione. Con un'istanza di Func
, utilizziamo Zip
per gestire gli elementi delle due raccolte C # in coppie. Se le serie differiscono per dimensioni, gli elementi extra delle serie più grandi verranno ignorati.
Per fare un esempio dal libro "C # in a Nutshell",
int[] numbers = { 3, 5, 7 };
string[] words = { "three", "five", "seven", "ignored" };
IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);
Produzione:
3 = tre
5 = cinque
7 = sette
GroupJoin con variabile range esterno
Customer[] customers = Customers.ToArray();
Purchase[] purchases = Purchases.ToArray();
var groupJoinQuery =
from c in customers
join p in purchases on c.ID equals p.CustomerID
into custPurchases
select new
{
CustName = c.Name,
custPurchases
};
ElementAt e ElementAtOrDefault
ElementAt
restituirà l'oggetto all'indice n
. Se n
non rientra nell'intervallo dell'enumerabile, genera un'eccezione ArgumentOutOfRangeException
.
int[] numbers = { 1, 2, 3, 4, 5 };
numbers.ElementAt(2); // 3
numbers.ElementAt(10); // throws ArgumentOutOfRangeException
ElementAtOrDefault
restituirà l'elemento all'indice n
. Se n
non è compreso nell'intervallo enumerabile, restituisce un default(T)
.
int[] numbers = { 1, 2, 3, 4, 5 };
numbers.ElementAtOrDefault(2); // 3
numbers.ElementAtOrDefault(10); // 0 = default(int)
Sia ElementAt
che ElementAtOrDefault
sono ottimizzati per quando la sorgente è un IList<T>
e in questi casi verrà utilizzata l'indicizzazione normale.
Si noti che per ElementAt
, se l'indice fornito è maggiore della dimensione del IList<T>
, l'elenco dovrebbe (ma tecnicamente non è garantito) generare una ArgumentOutOfRangeException
.
Quantificatori di Linq
Le operazioni Quantifier restituiscono un valore booleano se alcuni o tutti gli elementi di una sequenza soddisfano una condizione. In questo articolo vedremo alcuni comuni scenari LINQ su oggetti in cui possiamo utilizzare questi operatori. Esistono 3 operazioni Quantifiers che possono essere utilizzate in LINQ:
All
- utilizzato per determinare se tutti gli elementi di una sequenza soddisfano una condizione. Per esempio:
int[] array = { 10, 20, 30 };
// Are all elements >= 10? YES
array.All(element => element >= 10);
// Are all elements >= 20? NO
array.All(element => element >= 20);
// Are all elements < 40? YES
array.All(element => element < 40);
Any
: utilizzato per determinare se alcuni elementi di una sequenza soddisfano una condizione. Per esempio:
int[] query=new int[] { 2, 3, 4 }
query.Any (n => n == 3);
Contains
: utilizzato per determinare se una sequenza contiene un elemento specificato. Per esempio:
//for int array
int[] query =new int[] { 1,2,3 };
query.Contains(1);
//for string array
string[] query={"Tom","grey"};
query.Contains("Tom");
//for a string
var stringValue="hello";
stringValue.Contains("h");
Unire più sequenze
Considerare le entità Customer
, Purchase
e PurchaseItem
come segue:
public class Customer
{
public string Id { get; set } // A unique Id that identifies customer
public string Name {get; set; }
}
public class Purchase
{
public string Id { get; set }
public string CustomerId {get; set; }
public string Description { get; set; }
}
public class PurchaseItem
{
public string Id { get; set }
public string PurchaseId {get; set; }
public string Detail { get; set; }
}
Prendi in considerazione i seguenti dati di esempio per le entità sopra indicate:
var customers = new List<Customer>()
{
new Customer() {
Id = Guid.NewGuid().ToString(),
Name = "Customer1"
},
new Customer() {
Id = Guid.NewGuid().ToString(),
Name = "Customer2"
}
};
var purchases = new List<Purchase>()
{
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[0].Id,
Description = "Customer1-Purchase1"
},
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[0].Id,
Description = "Customer1-Purchase2"
},
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[1].Id,
Description = "Customer2-Purchase1"
},
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[1].Id,
Description = "Customer2-Purchase2"
}
};
var purchaseItems = new List<PurchaseItem>()
{
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[0].Id,
Detail = "Purchase1-PurchaseItem1"
},
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[1].Id,
Detail = "Purchase2-PurchaseItem1"
},
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[1].Id,
Detail = "Purchase2-PurchaseItem2"
},
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[3].Id,
Detail = "Purchase3-PurchaseItem1"
}
};
Ora, considera la seguente query linq:
var result = from c in customers
join p in purchases on c.Id equals p.CustomerId // first join
join pi in purchaseItems on p.Id equals pi.PurchaseId // second join
select new
{
c.Name, p.Description, pi.Detail
};
Per generare il risultato della query precedente:
foreach(var resultItem in result)
{
Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");
}
L'output della query sarebbe:
Customer1, Customer1-Purchase1, Purchase1-PurchaseItem1
Customer1, Customer1-Purchase2, Purchase2-PurchaseItem1
Customer1, Customer1-Purchase2, Purchase2-PurchaseItem2
Customer2, Customer2-Purchase2, Purchase3-PurchaseItem1
Partecipare a più chiavi
PropertyInfo[] stringProps = typeof (string).GetProperties();//string properties
PropertyInfo[] builderProps = typeof(StringBuilder).GetProperties();//stringbuilder properties
var query =
from s in stringProps
join b in builderProps
on new { s.Name, s.PropertyType } equals new { b.Name, b.PropertyType }
select new
{
s.Name,
s.PropertyType,
StringToken = s.MetadataToken,
StringBuilderToken = b.MetadataToken
};
Si noti che i tipi anonimi in join
sopra devono contenere le stesse proprietà poiché gli oggetti sono considerati uguali solo se tutte le loro proprietà sono uguali. Altrimenti la query non verrà compilata.
Seleziona con Func selettore - Utilizzare per ottenere il ranking degli elementi
Sui sovraccarichi dei metodi di estensione Select
passa anche l' index
dell'elemento corrente nella raccolta select
. Questi sono alcuni usi di esso.
Ottieni il "numero di riga" degli articoli
var rowNumbers = collection.OrderBy(item => item.Property1)
.ThenBy(item => item.Property2)
.ThenByDescending(item => item.Property3)
.Select((item, index) => new { Item = item, RowNumber = index })
.ToList();
Ottieni il rango di un oggetto all'interno del suo gruppo
var rankInGroup = collection.GroupBy(item => item.Property1)
.OrderBy(group => group.Key)
.SelectMany(group => group.OrderBy(item => item.Property2)
.ThenByDescending(item => item.Property3)
.Select((item, index) => new
{
Item = item,
RankInGroup = index
})).ToList();
Ottieni la classifica dei gruppi (noto anche in Oracle come dense_rank)
var rankOfBelongingGroup = collection.GroupBy(item => item.Property1)
.OrderBy(group => group.Key)
.Select((group, index) => new
{
Items = group,
Rank = index
})
.SelectMany(v => v.Items, (s, i) => new
{
Item = i,
DenseRank = s.Rank
}).ToList();
Per testare questo è possibile utilizzare:
public class SomeObject
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
public override string ToString()
{
return string.Join(", ", Property1, Property2, Property3);
}
}
E i dati:
List<SomeObject> collection = new List<SomeObject>
{
new SomeObject { Property1 = 1, Property2 = 1, Property3 = 1},
new SomeObject { Property1 = 1, Property2 = 2, Property3 = 1},
new SomeObject { Property1 = 1, Property2 = 2, Property3 = 2},
new SomeObject { Property1 = 2, Property2 = 1, Property3 = 1},
new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
new SomeObject { Property1 = 2, Property2 = 3, Property3 = 1}
};
TakeWhile
TakeWhile
restituisce elementi da una sequenza purché la condizione sia vera
int[] list = { 1, 10, 40, 50, 44, 70, 4 };
var result = list.TakeWhile(item => item < 50).ToList();
// result = { 1, 10, 40 }
Somma
Il metodo di estensione Enumerable.Sum
calcola la somma dei valori numerici.
Nel caso in cui gli elementi della raccolta siano essi stessi numeri, è possibile calcolare la somma direttamente.
int[] numbers = new int[] { 1, 4, 6 };
Console.WriteLine( numbers.Sum() ); //outputs 11
Nel caso in cui il tipo degli elementi sia di tipo complesso, è possibile utilizzare un'espressione lambda per specificare il valore da calcolare:
var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );
Il metodo di estensione somma può calcolare con i seguenti tipi:
- Int32
- Int64
- singolo
- Doppio
- Decimale
Nel caso in cui la tua raccolta contenga tipi annullabili, puoi utilizzare l'operatore null-coalescing per impostare un valore predefinito per elementi null:
int?[] numbers = new int?[] { 1, null, 6 };
Console.WriteLine( numbers.Sum( number => number ?? 0 ) ); //outputs 7
ToLookup
ToLookup restituisce una struttura dati che consente l'indicizzazione. È un metodo di estensione. Produce un'istanza di ILookup che può essere indicizzata o enumerata utilizzando un ciclo foreach. Le voci sono combinate in raggruppamenti per ogni chiave. - dotnetperls
string[] array = { "one", "two", "three" };
//create lookup using string length as key
var lookup = array.ToLookup(item => item.Length);
//join the values whose lengths are 3
Console.WriteLine(string.Join(",",lookup[3]));
//output: one,two
Un altro esempio:
int[] array = { 1,2,3,4,5,6,7,8 };
//generate lookup for odd even numbers (keys will be 0 and 1)
var lookup = array.ToLookup(item => item % 2);
//print even numbers after joining
Console.WriteLine(string.Join(",",lookup[0]));
//output: 2,4,6,8
//print odd numbers after joining
Console.WriteLine(string.Join(",",lookup[1]));
//output: 1,3,5,7
Costruisci i tuoi operatori Linq per IEnumerable
Una delle grandi cose di Linq è che è così facile da estendere. Hai solo bisogno di creare un metodo di estensione il cui argomento è IEnumerable<T>
.
public namespace MyNamespace
{
public static class LinqExtensions
{
public static IEnumerable<List<T>> Batch<T>(this IEnumerable<T> source, int batchSize)
{
var batch = new List<T>();
foreach (T item in source)
{
batch.Add(item);
if (batch.Count == batchSize)
{
yield return batch;
batch = new List<T>();
}
}
if (batch.Count > 0)
yield return batch;
}
}
}
Questo esempio suddivide gli elementi in un oggetto IEnumerable<T>
in elenchi di dimensioni fisse, l'ultimo elenco contenente il resto degli elementi. Si noti come l'oggetto a cui viene applicato il metodo di estensione viene passato in (argomento source
) come argomento iniziale utilizzando la parola chiave this
. Quindi la parola chiave yield
viene utilizzata per generare l'elemento successivo nell'output IEnumerable<T>
prima di continuare con l'esecuzione da quel punto (vedere la parola chiave yield ).
Questo esempio verrebbe utilizzato nel tuo codice in questo modo:
//using MyNamespace;
var items = new List<int> { 2, 3, 4, 5, 6 };
foreach (List<int> sublist in items.Batch(3))
{
// do something
}
Nel primo ciclo, il sottolista sarebbe {2, 3, 4}
e il secondo {5, 6}
.
I metodi LinQ personalizzati possono anche essere combinati con i metodi LinQ standard. per esempio:
//using MyNamespace;
var result = Enumerable.Range(0, 13) // generate a list
.Where(x => x%2 == 0) // filter the list or do something other
.Batch(3) // call our extension method
.ToList() // call other standard methods
Questa query restituirà numeri pari raggruppati in batch con una dimensione di 3: {0, 2, 4}, {6, 8, 10}, {12}
Ricorda che hai bisogno di using MyNamespace;
linea per poter accedere al metodo di estensione.
Usando SelectMany invece di cicli annidati
Dato 2 elenchi
var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "1", "2", "3", "4" };
se vuoi esportare tutte le permutazioni puoi usare loop annidati come
var result = new List<string>();
foreach (var s1 in list1)
foreach (var s2 in list2)
result.Add($"{s1}{s2}");
Usando SelectMany puoi fare la stessa operazione di
var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}", x, y)).ToList();
Any and First (OrDefault): best practice
Non spiegherò cosa fa Any
e FirstOrDefault
perché ci sono già due buoni esempi su di loro. Vedere Any and First, FirstOrDefault, Last, LastOrDefault, Single e SingleOrDefault per ulteriori informazioni.
Un modello che vedo spesso nel codice che dovrebbe essere evitato è
if (myEnumerable.Any(t=>t.Foo == "Bob"))
{
var myFoo = myEnumerable.First(t=>t.Foo == "Bob");
//Do stuff
}
Potrebbe essere scritto in modo più efficiente come questo
var myFoo = myEnumerable.FirstOrDefault(t=>t.Foo == "Bob");
if (myFoo != null)
{
//Do stuff
}
Utilizzando il secondo esempio, la raccolta viene ricercata una sola volta e restituisce lo stesso risultato della prima. La stessa idea può essere applicata a Single
.
GroupBy Sum e Count
Prendiamo una classe di esempio:
public class Transaction
{
public string Category { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
}
Ora, consideriamo un elenco di transazioni:
var transactions = new List<Transaction>
{
new Transaction { Category = "Saving Account", Amount = 56, Date = DateTime.Today.AddDays(1) },
new Transaction { Category = "Saving Account", Amount = 10, Date = DateTime.Today.AddDays(-10) },
new Transaction { Category = "Credit Card", Amount = 15, Date = DateTime.Today.AddDays(1) },
new Transaction { Category = "Credit Card", Amount = 56, Date = DateTime.Today },
new Transaction { Category = "Current Account", Amount = 100, Date = DateTime.Today.AddDays(5) },
};
Se si desidera calcolare la somma saggia della categoria di importo e contare, è possibile utilizzare GroupBy come segue:
var summaryApproach1 = transactions.GroupBy(t => t.Category)
.Select(t => new
{
Category = t.Key,
Count = t.Count(),
Amount = t.Sum(ta => ta.Amount),
}).ToList();
Console.WriteLine("-- Summary: Approach 1 --");
summaryApproach1.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));
In alternativa, puoi farlo in un solo passaggio:
var summaryApproach2 = transactions.GroupBy(t => t.Category, (key, t) =>
{
var transactionArray = t as Transaction[] ?? t.ToArray();
return new
{
Category = key,
Count = transactionArray.Length,
Amount = transactionArray.Sum(ta => ta.Amount),
};
}).ToList();
Console.WriteLine("-- Summary: Approach 2 --");
summaryApproach2.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));
L'output per entrambe le query sopra sarebbe lo stesso:
Categoria: Conto di risparmio, Quantità: 66, Numero: 2
Categoria: Carta di credito, quantità: 71, Count: 2
Categoria: conto corrente, importo: 100, numero: 1
Inverso
- Inverte l'ordine degli elementi in una sequenza.
- Se non ci sono elementi lancia una
ArgumentNullException: source is null.
Esempio:
// Create an array.
int[] array = { 1, 2, 3, 4 }; //Output:
// Call reverse extension method on the array. //4
var reverse = array.Reverse(); //3
// Write contents of array to screen. //2
foreach (int value in reverse) //1
Console.WriteLine(value);
Esempio di codice in tempo reale
Ricorda che Reverse()
può funzionare in modo diverso a seconda dell'ordine della catena delle istruzioni LINQ.
//Create List of chars
List<int> integerlist = new List<int>() { 1, 2, 3, 4, 5, 6 };
//Reversing the list then taking the two first elements
IEnumerable<int> reverseFirst = integerlist.Reverse<int>().Take(2);
//Taking 2 elements and then reversing only thos two
IEnumerable<int> reverseLast = integerlist.Take(2).Reverse();
//reverseFirst output: 6, 5
//reverseLast output: 2, 1
Esempio di codice in tempo reale
Reverse () funziona con il buffering di tutto quindi lo attraversa all'indietro, non è molto efficiente, ma nemmeno OrderBy da quella prospettiva.
In LINQ-to-Objects, ci sono operazioni di buffering (Reverse, OrderBy, GroupBy, ecc.) E operazioni di non buffering (Where, Take, Skip, ecc.).
Esempio: estensione non bufferizzata inversa
public static IEnumerable<T> Reverse<T>(this IList<T> list) {
for (int i = list.Count - 1; i >= 0; i--)
yield return list[i];
}
Esempio di codice in tempo reale
Questo metodo può incontrare problemi se si modifica l'elenco durante l'iterazione.
Enumerazione dell'enumerabile
L'interfaccia IEnumerable <T> è l'interfaccia di base per tutti gli enumeratori generici ed è una parte essenziale per la comprensione di LINQ. Nel suo nucleo, rappresenta la sequenza.
Questa interfaccia sottostante è ereditata da tutte le raccolte generiche, come Collection <T> , Array , List <T> , Dictionary <TKey, TValue> Class e HashSet <T> .
Oltre a rappresentare la sequenza, qualsiasi classe che eredita da IEnumerable <T> deve fornire un IEnumerator <T>. L'enumeratore espone l'iteratore per l'enumerabile, e queste due interfacce e idee interconnesse sono la fonte del detto "enumerare l'enumerabile".
"Enumerare l'enumerabile" è una frase importante. L'enumerabile è semplicemente una struttura su come iterare, non contiene oggetti materializzati. Ad esempio, durante l'ordinamento, un enumerable può contenere i criteri del campo da ordinare, ma l'utilizzo di .OrderBy()
in sé restituirà un IEnumerable <T> che sa solo come ordinare. L'uso di una chiamata che materializzerà gli oggetti, come in iterare l'insieme, è noto come enumerazione (ad esempio .ToList()
). Il processo di enumerazione utilizzerà la definizione enumerabile di come, al fine di muoversi attraverso le serie e restituire gli oggetti rilevanti (in ordine, filtrato, proiettato, ecc.).
Solo una volta che l'enumerabile è stato enumerato, ciò provoca la materializzazione degli oggetti, ovvero quando le metriche come la complessità temporale (quanto tempo occorre prendere in relazione alle dimensioni della serie) e la complessità spaziale (quanto spazio dovrebbe usare in relazione alle dimensioni della serie) possono essere misurato
La creazione della propria classe che eredita da IEnumerable <T> può essere un po 'complicata a seconda delle serie sottostanti che devono essere enumerabili. In generale, è meglio utilizzare una delle raccolte generiche esistenti. Detto questo, è anche possibile ereditare dall'interfaccia IEnumerable <T> senza avere una matrice definita come struttura sottostante.
Ad esempio, utilizzando la serie di Fibonacci come sequenza sottostante. Si noti che la chiamata a Where
semplicemente costruisce un oggetto IEnumerable
, e non è fino a quando una chiamata per enumerare quella enumerabile è fatta che tutti i valori sono materializzati.
void Main()
{
Fibonacci Fibo = new Fibonacci();
IEnumerable<long> quadrillionplus = Fibo.Where(i => i > 1000000000000);
Console.WriteLine("Enumerable built");
Console.WriteLine(quadrillionplus.Take(2).Sum());
Console.WriteLine(quadrillionplus.Skip(2).First());
IEnumerable<long> fibMod612 = Fibo.OrderBy(i => i % 612);
Console.WriteLine("Enumerable built");
Console.WriteLine(fibMod612.First());//smallest divisible by 612
}
public class Fibonacci : IEnumerable<long>
{
private int max = 90;
//Enumerator called typically from foreach
public IEnumerator GetEnumerator() {
long n0 = 1;
long n1 = 1;
Console.WriteLine("Enumerating the Enumerable");
for(int i = 0; i < max; i++){
yield return n0+n1;
n1 += n0;
n0 = n1-n0;
}
}
//Enumerable called typically from linq
IEnumerator<long> IEnumerable<long>.GetEnumerator() {
long n0 = 1;
long n1 = 1;
Console.WriteLine("Enumerating the Enumerable");
for(int i = 0; i < max; i++){
yield return n0+n1;
n1 += n0;
n0 = n1-n0;
}
}
}
Produzione
Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352
La forza nel secondo set (il fibMod612) è che anche se abbiamo fatto la chiamata per ordinare l'intero set di numeri di Fibonacci, poiché è stato preso solo un valore usando .First()
la complessità temporale era O (n) come solo 1 valore doveva essere confrontato durante l'esecuzione dell'algoritmo di ordinazione. Questo perché il nostro enumeratore ha chiesto solo 1 valore, e quindi non è stato necessario materializzare l'intero enumerabile. Se avessimo usato .Take(5)
anziché .First()
l'enumeratore avrebbe richiesto 5 valori e al massimo 5 valori avrebbero dovuto essere materializzati. Rispetto alla necessità di ordinare un intero set e quindi prendere i primi 5 valori, il principio di risparmiare un sacco di tempo e spazio di esecuzione.
Ordinato da
Ordina una collezione in base a un valore specificato.
Quando il valore è un numero intero , doppio o float inizia con il valore minimo , il che significa che si ottengono prima i valori negativi, che zero e afterwords i valori positivi (vedere Esempio 1).
Quando ordini per char, il metodo confronta i valori ascii dei caratteri per ordinare la raccolta (vedi Esempio 2).
Quando ordinate le stringhe, il metodo OrderBy le confronta confrontandole con CultureInfo, ma normalmente iniziano con la prima lettera dell'alfabeto (a, b, c ...).
Questo tipo di ordine è chiamato ascendente, se lo vuoi viceversa hai bisogno di scendere (vedi OrderByDescending).
Esempio 1:
int[] numbers = {2, 1, 0, -1, -2};
IEnumerable<int> ascending = numbers.OrderBy(x => x);
// returns {-2, -1, 0, 1, 2}
Esempio 2:
char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
IEnumerable<char> ascending = letters.OrderBy(x => x);
// returns { ' ', '!', '+', '1', '9', '?', 'A', 'B', 'Y', 'Z', '[', 'a', 'b', 'y', 'z', '{' }
Esempio:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var people = new[]
{
new Person {Name = "Alice", Age = 25},
new Person {Name = "Bob", Age = 21},
new Person {Name = "Carol", Age = 43}
};
var youngestPerson = people.OrderBy(x => x.Age).First();
var name = youngestPerson.Name; // Bob
OrderByDescending
Ordina una collezione in base a un valore specificato.
Quando il valore è un numero intero , doppio o float inizia con il valore massimo , il che significa che si ottengono prima i valori positivi, che zero e afterwords i valori negativi (vedere Esempio 1).
Quando ordini per char, il metodo confronta i valori ascii dei caratteri per ordinare la raccolta (vedi Esempio 2).
Quando si ordinano le stringhe, il metodo OrderBy li confronta dando un'occhiata al loro CultureInfo ma normalmente iniziando con l' ultima lettera dell'alfabeto (z, y, x, ...).
Questo tipo di ordine è chiamato discendente, se lo vuoi viceversa hai bisogno di salire (vedi OrderBy).
Esempio 1:
int[] numbers = {-2, -1, 0, 1, 2};
IEnumerable<int> descending = numbers.OrderByDescending(x => x);
// returns {2, 1, 0, -1, -2}
Esempio 2:
char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
IEnumerable<char> descending = letters.OrderByDescending(x => x);
// returns { '{', 'z', 'y', 'b', 'a', '[', 'Z', 'Y', 'B', 'A', '?', '9', '1', '+', '!', ' ' }
Esempio 3:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var people = new[]
{
new Person {Name = "Alice", Age = 25},
new Person {Name = "Bob", Age = 21},
new Person {Name = "Carol", Age = 43}
};
var oldestPerson = people.OrderByDescending(x => x.Age).First();
var name = oldestPerson.Name; // Carol
concat
Unisce due raccolte (senza rimuovere duplicati)
List<int> foo = new List<int> { 1, 2, 3 };
List<int> bar = new List<int> { 3, 4, 5 };
// Through Enumerable static class
var result = Enumerable.Concat(foo, bar).ToList(); // 1,2,3,3,4,5
// Through extension method
var result = foo.Concat(bar).ToList(); // 1,2,3,3,4,5
contiene
MSDN:
Determina se una sequenza contiene un elemento specificato utilizzando un
IEqualityComparer<T>
specificatoIEqualityComparer<T>
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var result1 = numbers.Contains(4); // true
var result2 = numbers.Contains(8); // false
List<int> secondNumberCollection = new List<int> { 4, 5, 6, 7 };
// Note that can use the Intersect method in this case
var result3 = secondNumberCollection.Where(item => numbers.Contains(item)); // will be true only for 4,5
Utilizzando un oggetto definito dall'utente:
public class Person
{
public string Name { get; set; }
}
List<Person> objects = new List<Person>
{
new Person { Name = "Nikki"},
new Person { Name = "Gilad"},
new Person { Name = "Phil"},
new Person { Name = "John"}
};
//Using the Person's Equals method - override Equals() and GetHashCode() - otherwise it
//will compare by reference and result will be false
var result4 = objects.Contains(new Person { Name = "Phil" }); // true
Utilizzo del sovraccarico Enumerable.Contains(value, comparer)
:
public class Compare : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Name == y.Name;
}
public int GetHashCode(Person codeh)
{
return codeh.Name.GetHashCode();
}
}
var result5 = objects.Contains(new Person { Name = "Phil" }, new Compare()); // true
Un uso intelligente di Contains
sarebbe quello di sostituire più clausole if
in una chiamata Contains
.
Quindi, invece di fare questo:
if(status == 1 || status == 3 || status == 4)
{
//Do some business operation
}
else
{
//Do something else
}
Fai questo:
if(new int[] {1, 3, 4 }.Contains(status)
{
//Do some business operaion
}
else
{
//Do something else
}