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

Live Demo su .NET Fiddle

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

Live Demo su .NET Fiddle

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);

Live Demo su .NET Fiddle

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);

Live Demo su .NET Fiddle

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

Live Demo su .NET Fiddle

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 una InvalidOperationException 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();

Live Demo su .NET Fiddle

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 il default(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();

Live Demo su .NET Fiddle

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 una InvalidOperationException 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 il default(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 una InvalidOperationException 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 una InvalidOperationException 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 il default(T) .
  • Se la sequenza contiene più di un elemento o più di un elemento che corrisponde al predicate fornito, viene generata una InvalidOperationException 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 il default(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 o SingleOrDefault per verificare se una sequenza contiene elementi, Any o Count sono più affidabili. Questo perché un valore di ritorno di default(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 ugualmente default(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 usa First ; come Single 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, il First 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

Live Demo su .NET Fiddle

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

Live Demo su .NET Fiddle

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

Live Demo su .NET Fiddle

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

Live Demo su .NET Fiddle

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

Live Demo su .NET Fiddle

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.

Demo live su .NET fiddle

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.

Live Demo su .NET Fiddle

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 passo prevSum = 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

  1. Il totale .Count
  2. La quantità di numeri pari
  3. 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.

Visualizza la demo

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

Visualizza la demo

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

Live Demo su .NET Fiddle

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

Demo dal vivo in .NET Fiddle

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> specificato IEqualityComparer<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
}


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow