Szukaj…


Wprowadzenie

LINQ jest akronimem oznaczającym L anguage IN tegrated Q aery. Jest to koncepcja, która integruje język zapytań, oferując spójny model pracy z danymi w różnych źródłach i formatach danych; używasz tych samych podstawowych wzorców kodowania, aby wyszukiwać i przekształcać dane w dokumentach XML, bazach danych SQL, zestawach danych ADO.NET, kolekcjach .NET i dowolnym innym formacie, dla którego dostępny jest dostawca LINQ.

Składnia

  • Składnia zapytania:

    • z <zmiennej zakresu> w <kolekcji>
    • [z <zmiennej zakresu> w <kolekcji>, ...]
    • <filtr, łączenie, grupowanie, agregowanie operatorów, ...> <wyrażenie lambda>
    • <wybierz lub zgrupuj według operatora> <sformułuj wynik>
  • Składnia metody:

    • Enumerable.Aggregate (func)
    • Enumerable.Aggregate (seed, func)
    • Enumerable.Aggregate (seed, func, resultSelector)
    • Enumerable.All (predicate)
    • Enumerable.Any ()
    • Enumerable.Any (predicate)
    • Enumerable.AsEnumerable ()
    • Enumerable.Average ()
    • Wyliczalny Średnia (selektor)
    • Enumerable.Cast <Result> ()
    • Enumerable.Concat (drugi)
    • Enumerable.Contains (wartość)
    • Enumerable.Contains (wartość, moduł porównujący)
    • Enumerable.Count ()
    • Enumerable.Count (predicate)
    • Enumerable.DefaultIfEmpty ()
    • Enumerable.DefaultIfEmpty (defaultValue)
    • Enumerable.Distinct ()
    • Enumerable.Distinct (porównaj)
    • Enumerable.ElementAt (indeks)
    • Enumerable.ElementAtOrDefault (indeks)
    • Enumerable.Empty ()
    • Enumerable.Except (second)
    • Enumerable.Except (drugi, moduł porównujący)
    • Enumerable.First ()
    • Enumerable.First (predicate)
    • Enumerable.FirstOrDefault ()
    • Enumerable.FirstOrDefault (predykat)
    • Enumerable.GroupBy (keySelector)
    • Enumerable.GroupBy (keySelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector)
    • Enumerable.GroupBy (keySelector, porównywarka)
    • Enumerable.GroupBy (keySelector, resultSelector, porównywarka)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector, porównywarka)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector, porównywarka)
    • Enumerable.Intersect (drugi)
    • Enumerable.Intersect (drugi, moduł porównujący)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, porównywarka)
    • Enumerable.Last ()
    • Enumerable.Last (predicate)
    • Enumerable.LastOrDefault ()
    • Enumerable.LastOrDefault (predykat)
    • Enumerable.LongCount ()
    • Enumerable.LongCount (predicate)
    • Enumerable.Max ()
    • Enumerable.Max (selektor)
    • Enumerable.Min ()
    • Enumerable.Min (selektor)
    • Enumerable.OfType <TResult> ()
    • Enumerable.OrderBy (keySelector)
    • Enumerable.OrderBy (keySelector, porównywarka)
    • Enumerable.OrderByDescending (keySelector)
    • Enumerable.OrderByDescending (keySelector, porównywarka)
    • Enumerable.Range (start, count)
    • Enumerable.Repeat (element, count)
    • Enumerable.Reverse ()
    • Enumerable.Select (selektor)
    • Enumerable.SelectMany (selektor)
    • Enumerable.SelectMany (collectionSelector, resultSelector)
    • Enumerable.SequenceEqual (drugi)
    • Enumerable.SequenceEqual (drugi, moduł porównujący)
    • Enumerable.Single ()
    • Enumerable.Single (predicate)
    • Enumerable.SingleOrDefault ()
    • Enumerable.SingleOrDefault (predykat)
    • Enumerable.Skip (count)
    • Enumerable.SkipWhile (predicate)
    • Enumerable.Sum ()
    • Enumerable.Sum (selektor)
    • Enumerable.Take (count)
    • Enumerable.TakeWhile (predicate)
    • orderEnumerable.ThenBy (keySelector)
    • orderEnumerable.ThenBy (keySelector, porównywarka)
    • orderEnumerable.ThenByDescending (keySelector)
    • orderEnumerable.ThenByDescending (keySelector, porównywarka)
    • Enumerable.ToArray ()
    • Enumerable.ToDictionary (keySelector)
    • Enumerable.ToDictionary (keySelector, elementSelector)
    • Enumerable.ToDictionary (keySelector, porównywarka)
    • Enumerable.ToDictionary (keySelector, elementSelector, porównywarka)
    • Enumerable.ToList ()
    • Enumerable.ToLookup (keySelector)
    • Enumerable.ToLookup (keySelector, elementSelector)
    • Enumerable.ToLookup (keySelector, porównywarka)
    • Enumerable.ToLookup (keySelector, elementSelector, porównywarka)
    • Enumerable.Union (drugi)
    • Enumerable.Union (drugi, moduł porównujący)
    • Enumerable.Where (predicate)
    • Enumerable.Zip (second, resultSelector)

Uwagi

Aby korzystać z zapytań LINQ, musisz zaimportować System.Linq .

Składnia metody jest bardziej wydajna i elastyczna, ale składnia zapytania może być prostsza i bardziej znana. Wszystkie zapytania napisane w składni zapytania są tłumaczone przez kompilator na składnię funkcjonalną, więc wydajność jest taka sama.

Obiekty zapytania nie są oceniane, dopóki nie zostaną użyte, więc można je zmienić lub dodać bez obniżenia wydajności.

Gdzie

Zwraca podzbiór elementów, dla których określony predykat jest dla nich prawdziwy.

List<string> trees = new List<string>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

Składnia metody

// Select all trees with name of length 3
var shortTrees = trees.Where(tree => tree.Length == 3); // Oak, Elm

Składnia zapytania

var shortTrees = from tree in trees
                 where tree.Length == 3
                 select tree; // Oak, Elm

Wybierz - Przekształcanie elementów

Wybierz pozwala zastosować transformację do każdego elementu w dowolnej strukturze danych implementującej IEnumerable.

Uzyskiwanie pierwszego znaku każdego ciągu z poniższej listy:

List<String> trees = new List<String>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

Używanie regularnej składni (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);
}

Wynik:

O
b
b
mi
H.
M.

Wersja demonstracyjna na żywo .NET Fiddle

Korzystanie ze składni zapytania LINQ

initials = from tree in trees
           select tree.Substring(0, 1);

Metody łączenia

Wiele funkcji LINQ działa zarówno na IEnumerable<TSource> i zwraca IEnumerable<TResult> . Parametry typu TSource i TResult mogą, ale nie muszą odnosić się do tego samego typu, w zależności od metody i przekazanych do niej funkcji.

Oto kilka przykładów

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
)

Podczas gdy niektóre metody łączenia łańcuchów mogą wymagać przetworzenia całego zestawu przed przejściem dalej, LINQ korzysta z odroczonego wykonania przy użyciu MSDN zwrotu, który tworzy Enumerable i Enumerator za kulisami. Proces tworzenia łańcuchów w LINQ polega zasadniczo na zbudowaniu elementu wymiennego (iteratora) dla oryginalnego zestawu - który jest odroczony - dopóki nie zostanie zmaterializowany przez wyliczenie elementu wymiennego .

Pozwala to na płynne łączenie tych funkcji w wiki , gdzie jedna funkcja może działać bezpośrednio na skutek innej. Tego stylu kodu można użyć do wykonania wielu operacji opartych na sekwencji w jednej instrukcji.

Na przykład możliwe jest połączenie Select , Where i OrderBy celu przekształcenia, filtrowania i sortowania sekwencji w pojedynczej instrukcji.

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

Wynik:

2)
4
8

Wersja demonstracyjna na żywo .NET Fiddle

Wszelkie funkcje, które zarówno rozszerzają, jak i zwracają ogólny typ IEnumerable<T> mogą być używane jako klauzule łańcuchowe w pojedynczej instrukcji. Ten styl płynnego programowania jest potężny i należy go wziąć pod uwagę przy tworzeniu własnych metod rozszerzenia .

Zasięg i powtórzenie

Do generowania prostych sekwencji można użyć metod statycznych Range and Repeat w Enumerable .

Zasięg

Enumerable.Range() generuje sekwencję liczb całkowitych o podanej wartości początkowej i liczbie.

// Generate a collection containing the numbers 1-100 ([1, 2, 3, ..., 98, 99, 100])
var range = Enumerable.Range(1,100);

Wersja demonstracyjna na żywo .NET Fiddle

Powtarzać

Enumerable.Repeat() generuje sekwencję powtarzających się elementów na podstawie elementu i wymaganej liczby powtórzeń.

// Generate a collection containing "a", three times (["a","a","a"])
var repeatedValues = Enumerable.Repeat("a", 3);

Wersja demonstracyjna na żywo .NET Fiddle

Skip and Take

Metoda Skip zwraca kolekcję wykluczającą liczbę elementów z początku kolekcji źródłowej. Liczba wykluczonych pozycji to liczba podana jako argument. Jeśli w kolekcji jest mniej elementów niż podano w argumencie, zwracana jest pusta kolekcja.

Metoda Take zwraca kolekcję zawierającą wiele elementów z początku kolekcji źródłowej. Liczba uwzględnionych elementów to liczba podana jako argument. Jeśli w kolekcji jest mniej elementów niż podano w argumencie, zwrócona kolekcja będzie zawierać te same elementy, co kolekcja źródłowa.

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

Wersja demonstracyjna na żywo .NET Fiddle

Pomiń i zabierz są powszechnie używane razem do dzielenia wyników, na przykład:

IEnumerable<T> GetPage<T>(IEnumerable<T> collection, int pageNumber, int resultsPerPage) {
    int startIndex = (pageNumber - 1) * resultsPerPage;
    return collection.Skip(startIndex).Take(resultsPerPage);
}

Ostrzeżenie: LINQ to Entities obsługuje tylko Skip w zamówionych zapytaniach . Jeśli spróbujesz użyć funkcji Skip bez zamówienia, otrzymasz NotSupportedException z komunikatem „Metoda Skip” jest obsługiwana tylko dla posortowanych danych wejściowych w LINQ do encji. Metodę „OrderBy” należy wywołać przed metodą „Skip”. ”

Najpierw FirstOrDefault, Last, LastOrDefault, Single i SingleOrDefault

Wszystkie sześć metod zwraca pojedynczą wartość typu sekwencji i można je wywoływać z predykatem lub bez niego.

W zależności od liczby elementów pasujących do predicate lub, jeśli nie predicate , liczby elementów w sekwencji źródłowej, zachowują się one w następujący sposób:

Pierwszy()

  • Zwraca pierwszy element sekwencji lub pierwszy element pasujący do podanego predicate .
  • Jeśli sekwencja nie zawiera elementów, InvalidOperationException jest InvalidOperationException z komunikatem: „Sekwencja nie zawiera elementów”.
  • Jeśli sekwencja nie zawiera elementów pasujących do podanego predicate , InvalidOperationException jest InvalidOperationException z komunikatem „Sekwencja nie zawiera pasującego elementu”.

Przykład

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

Wersja demonstracyjna na żywo .NET Fiddle

FirstOrDefault ()

  • Zwraca pierwszy element sekwencji lub pierwszy element pasujący do podanego predicate .
  • Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego predicate , zwraca domyślną wartość typu sekwencji przy użyciu default(T) .

Przykład

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

Wersja demonstracyjna na żywo .NET Fiddle

Ostatni, ubiegły, zeszły()

  • Zwraca ostatni element sekwencji lub ostatni element pasujący do podanego predicate .
  • Jeśli sekwencja nie zawiera elementów, InvalidOperationException jest InvalidOperationException z komunikatem „Sekwencja nie zawiera elementów”.
  • Jeśli sekwencja nie zawiera elementów pasujących do podanego predicate , InvalidOperationException jest InvalidOperationException z komunikatem „Sekwencja nie zawiera pasującego elementu”.

Przykład

// 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 ()

  • Zwraca ostatni element sekwencji lub ostatni element pasujący do podanego predicate .
  • Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego predicate , zwraca domyślną wartość typu sekwencji przy użyciu default(T) .

Przykład

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

Pojedynczy()

  • Jeśli sekwencja zawiera dokładnie jeden element lub dokładnie jeden element pasujący do podanego predicate , ten element jest zwracany.
  • Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego predicate , InvalidOperationException jest InvalidOperationException z komunikatem „Sekwencja nie zawiera elementów”.
  • Jeśli sekwencja zawiera więcej niż jeden element lub więcej niż jeden element pasujący do podanego predicate , InvalidOperationException jest InvalidOperationException z komunikatem „Sekwencja zawiera więcej niż jeden element”.
  • Uwaga: aby ocenić, czy sekwencja zawiera dokładnie jeden element, należy wyliczyć najwyżej dwa elementy.

Przykład

// 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 ()

  • Jeśli sekwencja zawiera dokładnie jeden element lub dokładnie jeden element pasujący do podanego predicate , ten element jest zwracany.
  • Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego predicate , zwracana jest default(T) .
  • Jeśli sekwencja zawiera więcej niż jeden element lub więcej niż jeden element pasujący do podanego predicate , InvalidOperationException jest InvalidOperationException z komunikatem „Sekwencja zawiera więcej niż jeden element”.
  • Jeśli sekwencja nie zawiera elementów pasujących do podanego predicate , zwraca domyślną wartość typu sekwencji za pomocą default(T) .
  • Uwaga: aby ocenić, czy sekwencja zawiera dokładnie jeden element, należy wyliczyć najwyżej dwa elementy.

Przykład

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

Rekomendacje

  • Chociaż możesz użyć FirstOrDefault , LastOrDefault lub SingleOrDefault aby sprawdzić, czy sekwencja zawiera jakieś elementy, Any lub Count są bardziej niezawodne. Wynika to z faktu, że zwracana wartość default(T) z jednej z tych trzech metod nie dowodzi, że sekwencja jest pusta, ponieważ wartość pierwszego / ostatniego / pojedynczego elementu sekwencji może być równie default(T)

  • Zdecyduj, które metody najlepiej pasują do Twojego kodu. Na przykład użyj opcji Single tylko wtedy, gdy musisz upewnić się, że w kolekcji znajduje się jeden element pasujący do predykatu - w przeciwnym razie użyj opcji First ; jako Single wyrzuć wyjątek, jeśli sekwencja ma więcej niż jeden pasujący element. Dotyczy to oczywiście także części „* OrDefault”.

  • Odnośnie wydajności: Chociaż często właściwe jest upewnienie się, że jest tylko jeden element ( Single ) lub, albo tylko jeden, albo zero ( SingleOrDefault ), zwrócone przez zapytanie, obie te metody wymagają więcej, a często całości, kolekcji do sprawdzenia, aby upewnić się, że nie ma drugiego dopasowania do zapytania. Jest to inaczej niż zachowanie na przykład metody First , którą można spełnić po znalezieniu pierwszego dopasowania.

Z wyjątkiem

Metoda Except zwraca zestaw elementów, które są zawarte w pierwszej kolekcji, ale nie są zawarte w drugiej. Domyślny IEqualityComparer służy do porównywania elementów w dwóch zestawach. Istnieje przeciążenie, które przyjmuje argument IEqualityComparer jako argument.

Przykład:

int[] first = { 1, 2, 3, 4 };
int[] second = { 0, 2, 3, 5 };

IEnumerable<int> inFirstButNotInSecond = first.Except(second);
// inFirstButNotInSecond = { 1, 4 }

Wynik:

1
4

Wersja demonstracyjna na żywo .NET Fiddle

W tym przypadku .Except(second) wyklucza elementy zawarte w second tablicy, a mianowicie 2 i 3 (0 i 5 nie są zawarte w first tablicy i są pomijane).

Zauważ, że „ Except oznacza Distinct (tzn. Usuwa powtarzające się elementy). Na przykład:

int[] third = { 1, 1, 1, 2, 3, 4 };

IEnumerable<int> inThirdButNotInSecond = third.Except(second);
// inThirdButNotInSecond = { 1, 4 }

Wynik:

1
4

Wersja demonstracyjna na żywo .NET Fiddle

W takim przypadku elementy 1 i 4 są zwracane tylko raz.


Wdrożenie IEquatable lub zapewnienie funkcji IEqualityComparer pozwoli na użycie innej metody porównywania elementów. Należy zauważyć, że metoda GetHashCode powinna również zostać przesłonięta, aby zwróciła identyczny kod skrótu dla object które są identyczne zgodnie z implementacją IEquatable .

Przykład z 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));
    }
}

Wynik:

Chanuka

Wersja demonstracyjna na żywo .NET Fiddle

SelectMany: Spłaszczanie sekwencji sekwencji

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 }

Użyj SelectMany() jeśli masz, lub tworzysz sekwencję sekwencji, ale chcesz, aby wynik był jedną długą sekwencją.

W składni zapytania LINQ:

var sequence = from subSequence in sequenceOfSequences
               from item in subSequence
               select item;

Jeśli masz kolekcję kolekcji i chciałbyś mieć możliwość pracy z danymi z kolekcji rodzica i dziecka w tym samym czasie, jest to również możliwe dzięki SelectMany .

Zdefiniujmy proste klasy

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

Załóżmy, że mamy następującą kolekcję.

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"
            }
        }
    }
};

Teraz chcemy wybrać komentarze Content wraz z Id BlogPost powiązanym z tym komentarzem. W tym celu możemy zastosować odpowiednie przeciążenie SelectMany .

var commentsWithIds = posts.SelectMany(p => p.Comments, (post, comment) => new { PostId = post.Id, CommentContent = comment.Content });

Nasze commentsWithIds wygląda

{
    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"
}

Wybierz wiele

Metoda SelectMany linq „spłaszcza” IEnumerable<IEnumerable<T>> w IEnumerable<T> . Wszystkie elementy T w instancjach IEnumerable zawartych w źródłowym IEnumerable zostaną połączone w jeden IEnumerable .

var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }

Jeśli użyjesz funkcji selektora, która przekształca elementy wejściowe w sekwencje, wynikiem będą elementy tych sekwencji zwracane jeden po drugim.

Zauważ, że w przeciwieństwie do Select() liczba elementów na wyjściu nie musi być taka sama jak na wejściu.

Bardziej prawdziwy przykład ze świata

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

Wynik:

Kok
Jacek
Jim
Jan

Wersja demonstracyjna na żywo .NET Fiddle

Wszystko

All służy do sprawdzania, czy wszystkie elementy kolekcji spełniają warunek, czy nie.
zobacz także: .Any

1. Pusty parametr

Wszystkie : nie można używać z pustym parametrem.

2. Wyrażenie lambda jako parametr

Wszystko : Zwraca true jeśli wszystkie elementy kolekcji spełniają wyrażenie lambda, a false przeciwnym razie:

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. Pusta kolekcja

Wszystko : Zwraca wartość true jeśli kolekcja jest pusta i podano wyrażenie lambda:

var numbers = new List<int>();
bool result = numbers.All(i => i >= 0); // true

Uwaga: All zatrzyma iterację kolekcji, gdy tylko znajdzie element niespełniający warunku. Oznacza to, że kolekcja niekoniecznie zostanie w pełni wyliczona; zostanie tylko wyliczony na tyle daleko, aby znaleźć pierwszy element niespełniający warunku.

Kolekcja zapytań według typu / rzutuj elementy na typ

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

Korzystanie z 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

Korzystanie 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

Korzystanie z 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 

Unia

Łączy dwie kolekcje, aby utworzyć odrębną kolekcję przy użyciu domyślnego modułu porównującego równość

int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 2, 3, 4, 5 };

var allElement = numbers1.Union(numbers2);   // AllElement now contains 1,2,3,4,5

Wersja demonstracyjna na żywo .NET Fiddle

ŁĄCZY

Połączenia służą do łączenia różnych list lub tabel zawierających dane za pomocą wspólnego klucza.

Podobnie jak w SQL, w LINQ obsługiwane są następujące rodzaje złączeń:
Połączenia wewnętrzne, lewe, prawe, krzyżowe i pełne zewnętrzne .

W poniższych przykładach zastosowano dwie listy:

var first = new List<string>(){ "a","b","c"}; // Left data
var second = new List<string>(){ "a", "c", "d"}; // Right data

(Wewnętrzny) Dołącz

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"}

Lewe zewnętrzne połączenie

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

Right 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"}

Pełne połączenie zewnętrzne

var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);

// Result: {"a","a"}
//         {"b", null}  
//         {"c","c"}  
//         {null,"d"}

Praktyczny przykład

Powyższe przykłady mają prostą strukturę danych, dzięki czemu możesz skupić się na zrozumieniu różnych sprzężeń LINQ pod względem technicznym, ale w prawdziwym świecie masz tabele z kolumnami, które musisz połączyć.

W poniższym przykładzie użyto tylko jednej klasy Region , w rzeczywistości połączysz dwie lub więcej różnych tabel, które zawierają ten sam klucz (w tym przykładzie first i second są połączone za pomocą wspólnego klucza ID ).

Przykład: Rozważ następującą strukturę danych:

public class Region 
{
    public Int32 ID;
    public string RegionDescription;
    
    public Region(Int32 pRegionID, string pRegionDescription=null)
    {
        ID = pRegionID; RegionDescription = pRegionDescription;
    }
}

Teraz przygotuj dane (tj. Zapełnij danymi):

// 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")
                 }; 

Możesz zobaczyć, że w tym przykładzie first nie zawiera żadnych opisów regionów, więc chcesz dołączyć do nich od second . Wtedy łączenie wewnętrzne wyglądałoby następująco:

// 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"}  

Ten wynik stworzył anonimowe obiekty w locie, co jest w porządku, ale już stworzyliśmy odpowiednią klasę - więc możemy ją określić: Zamiast select new { f.ID, s.RegionDescription }; możemy powiedzieć select new Region(f.ID, s.RegionDescription); , które zwrócą te same dane, ale utworzą obiekty typu Region - które utrzymają zgodność z innymi obiektami.

Demo na żywo na skrzypcach .NET

Odrębny

Zwraca unikalne wartości z IEnumerable . Unikalność jest określana przy użyciu domyślnego modułu porównującego równość.

int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };

var distinct = array.Distinct();
// distinct = { 1, 2, 3, 4, 5 }

Aby porównać niestandardowy typ danych, musimy zaimplementować interfejs IEquatable<T> i zapewnić dla tego typu metody GetHashCode i Equals . Lub porównywarka równości może zostać nadpisana:

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

Grupa Przez jedno lub wiele pól

Załóżmy, że mamy jakiś model filmowy:

public class Film {
    public string Title { get; set; }
    public string Category { get; set; }
    public int Year { get; set; }
}

Grupuj według właściwości kategorii:

foreach (var grp in films.GroupBy(f => f.Category)) {
    var groupCategory = grp.Key;
    var numberOfFilmsInCategory = grp.Count();
}

Grupuj według kategorii i roku:

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

Korzystanie z Range z różnymi metodami Linq

Możesz użyć klasy Enumerable obok zapytań Linq, aby przekonwertować pętle na liniowce Linq one.

Wybierz przykład

W przeciwieństwie do robienia tego:

var asciiCharacters = new List<char>();
for (var x = 0; x < 256; x++)
{
    asciiCharacters.Add((char)x);
}

Możesz to zrobić:

var asciiCharacters = Enumerable.Range(0, 256).Select(a => (char) a);

Gdzie przykład

W tym przykładzie zostanie wygenerowanych 100 liczb, a nawet zostaną wyodrębnione

var evenNumbers = Enumerable.Range(1, 100).Where(a => a % 2 == 0);

Kolejność zapytań - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()

string[] names= { "mark", "steve", "adam" };

Rosnąco:

Składnia zapytania

var sortedNames =
    from name in names
    orderby name
    select name;

Metoda Składnia

var sortedNames = names.OrderBy(name => name);

sortedNames zawiera nazwy w następującej kolejności: „adam”, „mark”, „steve”

Malejąco:

Składnia zapytania

var sortedNames =
    from name in names
    orderby name descending
    select name;

Metoda Składnia

var sortedNames = names.OrderByDescending(name => name);

sortedNames zawiera nazwy w następującej kolejności: „steve”, „mark”, „adam”

Zamów według kilku pól

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

Składnia zapytania

var sortedPeople = from person in people
                   orderby person.LastName, person.FirstName, person.Age descending
                   select person;

Metoda Składnia

 sortedPeople = people.OrderBy(person => person.LastName)
                      .ThenBy(person => person.FirstName)
                      .ThenByDescending(person => person.Age);

Wynik

1. Adam Ackerman 29
2. Adam Ackerman 15
3. Phil Collins  28
4. Steve Collins 30

Podstawy

LINQ jest w dużej mierze korzystny przy wyszukiwaniu kolekcji (lub tablic).

Na przykład biorąc pod uwagę następujące przykładowe dane:

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

Możemy „zapytać” o te dane przy użyciu składni LINQ. Na przykład, aby odzyskać wszystkich uczniów, którzy mają dziś przekąskę:

var studentsWithSnacks = from s in classroom.Students
                         where s.HasSnack
                         select s;

Lub, aby pobrać uczniów z klasą 90 lub wyższą i zwrócić tylko ich nazwiska, a nie pełny obiekt Student :

var topStudentNames = from s in classroom.Students
                      where s.Grade >= 90
                      select s.Name;

Funkcja LINQ składa się z dwóch składni, które wykonują te same funkcje, mają prawie identyczną wydajność, ale są napisane zupełnie inaczej. Składnia w powyższym przykładzie nazywa się składnią zapytania . Poniższy przykład ilustruje jednak składnię metody . Te same dane zostaną zwrócone jak w powyższym przykładzie, ale sposób zapisu zapytania jest inny.

var topStudentNames = classroom.Students
                               .Where(s => s.Grade >= 90)
                               .Select(s => s.Name);

Grupuj według

GroupBy to prosty sposób na posortowanie IEnumerable<T> kolekcji elementów na osobne grupy.

Prosty przykład

W tym pierwszym przykładzie otrzymujemy dwie grupy, nieparzyste i parzyste.

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

Bardziej złożony przykład

Jako przykład weźmy pogrupowanie listy osób według wieku. Najpierw stworzymy obiekt Osoba, który ma dwie właściwości: Nazwę i Wiek.

public class Person
{
    public int Age {get; set;}
    public string Name {get; set;}
}

Następnie tworzymy naszą przykładową listę osób o różnych nazwiskach i grupach wiekowych.

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

Następnie tworzymy zapytanie LINQ, aby pogrupować naszą listę osób według wieku.

var query = people.GroupBy(x => x.Age);

Robiąc to, możemy zobaczyć Wiek dla każdej grupy i mieć listę każdej osoby w grupie.

foreach(var result in query)
{
    Console.WriteLine(result.Key);
                
    foreach(var person in result)
        Console.WriteLine(person.Name);
}

Daje to następujące wyniki:

20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith

Możesz zagrać w demo na żywo w .NET Fiddle

Każdy

Any służy do sprawdzania, czy jakikolwiek element kolekcji pasuje do warunku, czy nie.
zobacz także: .Wszystkie , Any i FirstOrDefault: najlepsze praktyki

1. Pusty parametr

Any : Zwraca wartość true jeśli kolekcja ma jakieś elementy, a wartość false jeśli kolekcja jest pusta:

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. Wyrażenie lambda jako parametr

Any : Zwraca wartość true jeśli kolekcja zawiera jeden lub więcej elementów spełniających warunek wyrażenia lambda:

var arrayOfStrings = new string[] { "a", "b", "c" };
arrayOfStrings.Any(item => item == "a");    // true
arrayOfStrings.Any(item => item == "d");    // false

3. Pusta kolekcja

Any : Zwraca false jeśli kolekcja jest pusta i podano wyrażenie lambda:

var numbers = new List<int>();
bool result = numbers.Any(i => i >= 0); // false

Uwaga: Any zatrzyma iterację kolekcji, gdy tylko znajdzie element spełniający warunek. Oznacza to, że kolekcja niekoniecznie zostanie w pełni wyliczona; zostanie tylko wyliczony na tyle daleko, aby znaleźć pierwszy element spełniający warunek.

Wersja demonstracyjna na żywo .NET Fiddle

ToDictionary

Za ToDictionary() metody LINQ ToDictionary() można wygenerować kolekcję Dictionary<TKey, TElement> na podstawie danego źródła IEnumerable<T> .

IEnumerable<User> users = GetUsers();
Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);

W tym przykładzie pojedynczy argument przekazany do ToDictionary jest typu Func<TSource, TKey> , który zwraca klucz dla każdego elementu.

Jest to zwięzły sposób na wykonanie następującej operacji:

Dictionary<int, User> usersById = new Dictionary<int User>();
foreach (User u in users) 
{
  usersById.Add(u.Id, u);
}

Możesz także przekazać drugi parametr do metody ToDictionary , która jest typu Func<TSource, TElement> i zwraca Value należy dodać dla każdego wpisu.

IEnumerable<User> users = GetUsers();
Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);

Możliwe jest również określenie IComparer który jest używany do porównywania kluczowych wartości. Może to być przydatne, gdy klucz jest łańcuchem i chcesz, aby pasował on bez rozróżniania wielkości liter.

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

Uwaga: metoda ToDictionary wymaga, aby wszystkie klucze były unikalne, nie może być duplikatów kluczy. Jeśli tak, zgłaszany jest wyjątek: ArgumentException: An item with the same key has already been added. Jeśli masz scenariusz, w którym wiesz, że będziesz mieć wiele elementów z tym samym kluczem, lepiej skorzystać z ToLookup .

Agregat

Aggregate Stosuje funkcję akumulatora w sekwencji.

int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
  • W pierwszym kroku prevSum = 1
  • Przy drugim prevSum = prevSum(at the first step) + 2
  • W i-tym kroku prevSum = prevSum(at the (i-1) step) + i-th element of the array kroku 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 !"

Drugie przeciążenie Aggregate otrzymuje również parametr seed , który jest początkową wartością akumulatora. Można tego użyć do obliczenia wielu warunków w kolekcji bez iteracji więcej niż jeden raz.

List<int> items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

Do kolekcji items chcemy obliczyć

  1. Łącznie .Count
  2. Liczba liczb parzystych
  3. Zbierz każdy czwarty przedmiot

Za pomocą Aggregate można to zrobić w następujący sposób:

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]

Zauważ, że użycie anonimowego typu jako materiału siewnego wymaga utworzenia nowego obiektu dla każdego elementu, ponieważ właściwości są tylko do odczytu. Korzystanie z jednego zwyczaj klasa może po prostu przypisać te informacje i nie new jest potrzebne (tylko przy podawaniu początkowej seed parametr

Definiowanie zmiennej w zapytaniu Linq (słowo kluczowe let)

Aby zdefiniować zmienną w wyrażeniu linq, możesz użyć słowa kluczowego let . Zwykle odbywa się to w celu przechowywania wyników pośrednich zapytań cząstkowych, na przykład:

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

Wynik:

Średnia liczb wynosi 4,5.
Wynik zapytania obejmuje liczbę 3 z kwadratem 9.
Wynik zapytania obejmuje liczbę 4 z kwadratem 16.
Wynik zapytania obejmuje liczbę 5 z kwadratem 25.
Wynik zapytania obejmuje liczbę 6 z kwadratem 36.
Wynik zapytania obejmuje liczbę 7 z kwadratem 49.
Wynik zapytania obejmuje liczbę 8 z kwadratem 64.
Wynik zapytania obejmuje liczbę 9 z kwadratem 81.

Zobacz demo

SkipWhile

SkipWhile() służy do wykluczania elementów aż do pierwszego SkipWhile() może to być sprzeczne z intuicją dla większości)

int[] list = { 42, 42, 6, 6, 6, 42 };
var result = list.SkipWhile(i => i == 42); 
// Result: 6, 6, 6, 42

DefaultIfEmpty

DefaultIfEmpty służy do zwracania elementu domyślnego, jeśli sekwencja nie zawiera żadnych elementów. Ten element może być domyślnym typem lub instancją tego typu zdefiniowaną przez użytkownika. Przykład:

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;

Wykorzystanie w lewych połączeniach :

Dzięki DefaultIfEmpty tradycyjne DefaultIfEmpty Linq może zwrócić domyślny obiekt, jeśli nie znaleziono dopasowania. W ten sposób działa jako lewy łącznik SQL. Przykład:

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

W przypadku użycia DefaultIfEmpty (bez określenia wartości domyślnej), co spowoduje brak pasujących elementów we właściwej kolejności, należy upewnić się, że obiekt nie ma null przed uzyskaniem dostępu do jego właściwości. W przeciwnym razie spowoduje to NullReferenceException . Przykład:

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 służy do porównywania dwóch IEnumerable<T> sekwencji IEnumerable<T> ze sobą.

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

Policz i LongCount

Count zwraca liczbę elementów w IEnumerable<T> . Count udostępnia także opcjonalny parametr predykatu, który pozwala filtrować elementy, które chcesz policzyć.

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 działa w taki sam sposób jak Count ale ma typ zwracany long i jest używany do zliczania IEnumerable<T> sekwencji IEnumerable<T> , które są dłuższe niż 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

Stopniowe budowanie zapytania

Ponieważ LINQ korzysta z odroczonego wykonywania , możemy mieć obiekt zapytania, który tak naprawdę nie zawiera wartości, ale zwróci wartości po ocenie. W ten sposób możemy dynamicznie budować zapytanie w oparciu o nasz przepływ kontroli i oceniać je, gdy skończymy:

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

Możemy warunkowo zastosować filtry:

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

Możemy dodać kolejność sortowania do zapytania na podstawie warunku:

    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.
    }

Nasze zapytanie można zdefiniować tak, aby zaczynało się od określonego punktu:

    query = query.Skip(start - 1);

i zdefiniowane, aby zwrócić określoną liczbę rekordów:

    if (count > -1) {
        query = query.Take(count);
    }
    return query;
}

Gdy mamy już obiekt zapytania, możemy ocenić wyniki za pomocą pętli foreach lub jednej z metod LINQ, która zwraca zestaw wartości, takich jak ToList lub ToArray :

SearchModel sm;

// populate the search model here
// ...

List<VehicleModel> list = BuildQuery(5, sm).ToList();

Zamek błyskawiczny

Metoda rozszerzenia Zip działa na dwie kolekcje. Łączy każdy element w dwóch seriach razem na podstawie pozycji. W instancji Func używamy Zip do obsługi elementów z dwóch kolekcji C # parami. Jeśli seria różni się rozmiarem, dodatkowe elementy większej serii zostaną zignorowane.

Aby wziąć przykład z książki „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);

Wynik:

3 = trzy
5 = pięć
7 = siedem

Zobacz demo

GroupJoin ze zmienną zakresu zewnętrznego

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 i ElementAtOrDefault

ElementAt zwróci element o indeksie n . Jeśli n nie mieści się w zakresie wyliczalnego, zgłasza ArgumentOutOfRangeException .

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAt(2);  // 3
numbers.ElementAt(10); // throws ArgumentOutOfRangeException

ElementAtOrDefault zwróci element o indeksie n . Jeśli n nie mieści się w zakresie wyliczalnego, zwraca wartość default(T) .

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAtOrDefault(2);  // 3
numbers.ElementAtOrDefault(10); // 0 = default(int)

Zarówno ElementAt jak i ElementAtOrDefault są zoptymalizowane pod kątem, gdy źródłem jest IList<T> i w takich przypadkach zostanie zastosowane normalne indeksowanie.

Zauważ, że dla ElementAt , jeśli podany indeks jest większy niż rozmiar IList<T> , lista powinna (ale nie jest technicznie gwarantowane) ElementAt ArgumentOutOfRangeException .

Kwantyfikatory Linq

Operacje kwantyfikatora zwracają wartość logiczną, jeśli niektóre lub wszystkie elementy w sekwencji spełniają warunek. W tym artykule zobaczymy kilka typowych scenariuszy LINQ to Objects, w których możemy użyć tych operatorów. Istnieją 3 operacje kwantyfikatorów, których można użyć w LINQ:

All - służy do określenia, czy wszystkie elementy w sekwencji spełniają warunek. Na przykład:

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 - służy do określenia, czy którykolwiek element w sekwencji spełnia warunek. Na przykład:

int[] query=new int[] { 2, 3, 4 }
query.Any (n => n == 3);

Contains - służy do ustalenia, czy sekwencja zawiera określony element. Na przykład:

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

Łączenie wielu sekwencji

Uwzględnij podmioty Customer , Purchase i PurchaseItem w następujący sposób:

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

Rozważ następujące przykładowe dane dla powyższych podmiotów:

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"
     }
 };

Teraz rozważ poniższe zapytanie 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
            };

Aby wyświetlić wynik powyższego zapytania:

foreach(var resultItem in result)
{
    Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");
}

Dane wyjściowe zapytania będą następujące:

Klient1, Klient1-Zakup1, Zakup1-ZakupItem1

Klient1, Klient1-Zakup2, Zakup2-ZakupItem1

Klient1, Klient1-Zakup2, Zakup2-ZakupItem2

Klient2, Klient2-Zakup2, Zakup3-ZakupItem1

Wersja demonstracyjna na żywo .NET Fiddle

Łączenie wielu kluczy

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

Zauważ, że anonimowe typy w powyższym join muszą zawierać te same właściwości, ponieważ obiekty są uważane za równe tylko wtedy, gdy wszystkie ich właściwości są równe. W przeciwnym razie zapytanie nie zostanie skompilowane.

Wybierz za pomocą Func selektor - służy do uzyskiwania rankingu elementów

Z przeciążeń metod rozszerzenia Select przechodzi również index bieżącego elementu w kolekcji, która jest select ed. Oto kilka jego zastosowań.

Uzyskaj „numer wiersza” elementów

var rowNumbers = collection.OrderBy(item => item.Property1)
                           .ThenBy(item => item.Property2)
                           .ThenByDescending(item => item.Property3)
                           .Select((item, index) => new { Item = item, RowNumber = index })
                           .ToList();

Uzyskaj rangę przedmiotu w grupie

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

Uzyskaj ranking grup (znany również w Oracle jako 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();

Do przetestowania tego możesz użyć:

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

I dane:

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 zwraca elementy z sekwencji, o ile warunek jest spełniony

int[] list = { 1, 10, 40, 50, 44, 70, 4 };
var result = list.TakeWhile(item => item < 50).ToList();
// result = { 1, 10, 40 }

Suma

Enumerable.Sum rozszerzenia Enumerable.Sum oblicza sumę wartości liczbowych.

W przypadku, gdy same elementy kolekcji są liczbami, można bezpośrednio obliczyć sumę.

int[] numbers = new int[] { 1, 4, 6 };
Console.WriteLine( numbers.Sum() ); //outputs 11

W przypadku, gdy typ elementów jest typem złożonym, możesz użyć wyrażenia lambda, aby określić wartość, którą należy obliczyć:

var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );

Metodę rozszerzenia sumy można obliczyć za pomocą następujących typów:

  • Int32
  • Int64
  • Pojedynczy
  • Podwójnie
  • Dziesiętny

Jeśli twoja kolekcja zawiera typy zerowalne, możesz użyć operatora zerowania koalescencji, aby ustawić wartość domyślną dla elementów zerowych:

int?[] numbers = new int?[] { 1, null, 6 };
Console.WriteLine( numbers.Sum( number => number ?? 0 ) ); //outputs 7

ToLookup

ToLookup zwraca strukturę danych, która umożliwia indeksowanie. Jest to metoda rozszerzenia. Tworzy instancję ILookup, którą można indeksować lub wyliczać za pomocą pętli foreach. Wpisy są łączone w grupy przy każdym kluczu. - 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

Inny przykład:

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

Zbuduj własne operatory Linq dla IEnumerable

Jedną z wielkich zalet Linq jest to, że można go łatwo rozbudować. Musisz tylko utworzyć metodę rozszerzenia, której argument to 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;
        }
    }
}

Ten przykład dzieli elementy w IEnumerable<T> na listy o ustalonym rozmiarze, przy czym ostatnia lista zawiera resztę elementów. Zwróć uwagę, w jaki sposób obiekt, do którego zastosowano metodę rozszerzenia, jest przekazywany ( source argumentu) jako argument początkowy przy użyciu this słowa kluczowego. Następnie słowo kluczowe yield służy do wyprowadzenia następnego elementu na wyjściu IEnumerable<T> przed kontynuowaniem wykonywania od tego momentu (patrz słowo kluczowe fed ).

Ten przykład zostałby użyty w twoim kodzie w następujący sposób:

//using MyNamespace;
var items = new List<int> { 2, 3, 4, 5, 6 };
foreach (List<int> sublist in items.Batch(3))
{
    // do something
}

W pierwszej pętli podlistą będzie {2, 3, 4} a w drugiej {5, 6} .

Niestandardowe metody LinQ można również łączyć ze standardowymi metodami LinQ. na przykład:

//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

To zapytanie zwróci liczby parzyste zgrupowane w partiach o rozmiarze 3: {0, 2, 4}, {6, 8, 10}, {12}

Pamiętaj, że potrzebujesz using MyNamespace; wiersz, aby mieć dostęp do metody rozszerzenia.

Używanie SelectMany zamiast zagnieżdżonych pętli

Biorąc pod uwagę 2 listy

var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "1", "2", "3", "4" };

jeśli chcesz wyprowadzić wszystkie permutacje, możesz użyć zagnieżdżonych pętli, takich jak

var result = new List<string>();
foreach (var s1 in list1)
    foreach (var s2 in list2)
        result.Add($"{s1}{s2}");

Za pomocą SelectMany możesz wykonać tę samą operację, co

var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}", x, y)).ToList();

Any and First (OrDefault) - najlepsza praktyka

Nie wyjaśnię, co robią Any i FirstOrDefault , ponieważ istnieją już dwa dobre przykłady na ich temat. Aby uzyskać więcej informacji, zobacz Dowolny i pierwszy, FirstOrDefault, Last, LastOrDefault, Single i SingleOrDefault .

Często widuję w kodzie wzór, którego należy unikać

if (myEnumerable.Any(t=>t.Foo == "Bob"))
{
    var myFoo = myEnumerable.First(t=>t.Foo == "Bob");
    //Do stuff
}

Można to napisać bardziej efektywnie w ten sposób

var myFoo = myEnumerable.FirstOrDefault(t=>t.Foo == "Bob");
if (myFoo != null)
{
    //Do stuff
}

Korzystając z drugiego przykładu, kolekcja jest przeszukiwana tylko raz i daje taki sam wynik jak pierwszy. Ten sam pomysł można zastosować do Single .

GroupBy Sum and Count

Weźmy przykładową klasę:

public class Transaction
{
    public string Category { get; set; }
    public DateTime Date { get; set; }
    public decimal Amount { get; set; }
}

Rozważmy teraz listę transakcji:

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

Jeśli chcesz obliczyć kategorię pod względem sumy i liczby, możesz użyć GroupBy w następujący sposób:

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

Alternatywnie możesz to zrobić w jednym kroku:

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

Dane wyjściowe dla obu powyższych zapytań byłyby takie same:

Kategoria: Konto oszczędnościowe, Kwota: 66, Liczba: 2

Kategoria: karta kredytowa, kwota: 71, liczba: 2

Kategoria: rachunek bieżący, kwota: 100, liczba: 1

Demo na żywo w .NET Fiddle

Rewers

  • Odwraca kolejność elementów w sekwencji.
  • Jeśli nie ma żadnych elementów, zgłasza ArgumentNullException: source is null.

Przykład:

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

Przykład kodu na żywo

Pamiętaj, że funkcja Reverse() może działać inaczej w zależności od kolejności łańcucha instrukcji 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

Przykład kodu na żywo

Funkcja Reverse () działa na buforowanie wszystkiego, a następnie przechodzenie przez nie do tyłu, co nie jest zbyt wydajne, ale z tego punktu widzenia OrderBy również nie jest.

W LINQ-to-Objects są operacje buforowania (Reverse, OrderBy, GroupBy itp.) Oraz operacje niebuforowane (Where, Take, Skip, itp.).

Przykład: nie buforujące rozszerzenie odwrotne

public static IEnumerable<T> Reverse<T>(this IList<T> list) {
    for (int i = list.Count - 1; i >= 0; i--) 
        yield return list[i];
}

Przykład kodu na żywo

Ta metoda może napotkać problemy, jeśli mutujesz listę podczas iteracji.

Wyliczanie wyliczalnego

Interfejs IEnumerable <T> jest interfejsem podstawowym wszystkich ogólnych modułów wyliczających i jest kwintesencją zrozumienia LINQ. W swoim rdzeniu reprezentuje sekwencję.

Ten podstawowy interfejs jest dziedziczony przez wszystkie ogólne kolekcje, takie jak Collection <T> , Array , List <T> , Dictionary <TKey, TValue> Class i HashSet <T> .

Oprócz reprezentowania sekwencji każda klasa dziedzicząca z IEnumerable <T> musi udostępnić IEnumerator <T>. Moduł wyliczający ujawnia iterator dla elementu wyliczeniowego, a te dwa wzajemnie powiązane interfejsy i idee są źródłem powiedzenia „wyliczenie elementu wyliczeniowego”.

„Wyliczanie wyliczalnego” jest ważnym zwrotem. Wyliczalna jest po prostu strukturą do iteracji, nie zawiera żadnych zmaterializowanych obiektów. Na przykład, podczas sortowania, wyliczanie może zawierać kryteria pola do sortowania, ale .OrderBy() użycie .OrderBy() zwróci IEnumerable <T>, który wie tylko jak sortować. Korzystanie z wywołania, które zmaterializuje obiekty, podobnie jak w iteracji zestawu, jest znane jako wyliczanie (na przykład .ToList() ). Proces wyliczania będzie wykorzystywał wymienną definicję tego, jak poruszać się po serii i zwracać odpowiednie obiekty (w kolejności, filtrowane, rzutowane itp.).

Dopiero po wyliczeniu wyliczenia powoduje materializację obiektów, to jest wtedy, gdy mierniki takie jak złożoność czasu (jak długo powinno to zająć w związku z rozmiarem szeregu) i złożoność przestrzenna (ile miejsca powinien użyć w związku z rozmiarem szeregu) być mierzonym.

Utworzenie własnej klasy, która dziedziczy po IEnumerable <T>, może być nieco skomplikowane w zależności od podstawowej serii, która musi być policzalna. Ogólnie najlepiej jest użyć jednej z istniejących kolekcji ogólnych. To powiedziawszy, możliwe jest również dziedziczenie z IEnumerable interfejsu <T> bez posiadania zdefiniowanej tablicy jako podstawowej struktury.

Na przykład, używając serii Fibonacciego jako podstawowej sekwencji. Zauważ, że wywołanie Where po prostu buduje IEnumerable , i dopiero po wywołaniu wyliczenia tego enumerable zostanie wykonana dowolna z tych wartości.

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

Wynik

Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352

Siła w drugim zbiorze (fibMod612) polega na tym, że chociaż wykonaliśmy wywołanie, aby zamówić cały nasz zestaw liczb Fibonacciego, ponieważ za pomocą .First() tylko jedną wartość, złożoność czasowa wynosiła O (n) jako tylko 1 wartość należy porównać podczas wykonywania algorytmu zamawiania. Wynika to z faktu, że nasz moduł wyliczający poprosił tylko o 1 wartość, a zatem nie trzeba było zmaterializować całego wyliczenia. Gdybyśmy użyli .Take(5) zamiast .First() wyliczający poprosiłby o 5 wartości, a najwyżej 5 wartości musiałoby się zmaterializować. W porównaniu z koniecznością zamówienia całego zestawu, a następnie wzięcia pierwszych 5 wartości, zasada oszczędza dużo czasu i miejsca wykonania.

Zamów przez

Zamawia kolekcję według określonej wartości.

Gdy wartość jest liczbą całkowitą , podwójną lub zmiennoprzecinkową , zaczyna się od wartości minimalnej , co oznacza, że najpierw otrzymujesz wartości ujemne, niż zero, a następnie wartości dodatnie (patrz Przykład 1).

Gdy zamawiasz za pomocą znaku, metoda porównuje wartości ascii znaków w celu posortowania kolekcji (patrz Przykład 2).

Podczas sortowania ciągów metoda OrderBy porównuje je, patrząc na ich CultureInfo, ale zwykle zaczyna się od pierwszej litery w alfabecie (a, b, c ...).

Tego rodzaju porządek nazywa się rosnąco, jeśli chcesz go odwrotnie, musisz zejść (patrz OrderByDescending).

Przykład 1:

int[] numbers = {2, 1, 0, -1, -2};
IEnumerable<int> ascending = numbers.OrderBy(x => x);
// returns {-2, -1, 0, 1, 2}

Przykład 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', '{' }

Przykład:

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

Zamawia kolekcję według określonej wartości.

Kiedy wartość jest liczbą całkowitą , podwójną lub zmiennoprzecinkową , zaczyna się od wartości maksymalnej , co oznacza, że najpierw otrzymujesz wartości dodatnie, niż zero, a następnie wartości ujemne (patrz Przykład 1).

Gdy zamawiasz za pomocą znaku, metoda porównuje wartości ascii znaków w celu posortowania kolekcji (patrz Przykład 2).

Podczas sortowania ciągów metoda OrderBy porównuje je, patrząc na ich CultureInfo, ale zwykle zaczyna się od ostatniej litery w alfabecie (z, y, x, ...).

Ten rodzaj porządku nazywa się malejącym, jeśli chcesz go na odwrót, musisz rosnąć (patrz OrderBy).

Przykład 1:

int[] numbers = {-2, -1, 0, 1, 2};
IEnumerable<int> descending = numbers.OrderByDescending(x => x);
// returns {2, 1, 0, -1, -2}

Przykład 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', '+', '!', ' ' }

Przykład 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

Scala dwie kolekcje (bez usuwania duplikatów)

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

Zawiera

MSDN:

Określa, czy sekwencja zawiera określony element, używając określonego 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

Korzystanie z obiektu zdefiniowanego przez użytkownika:

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

Korzystanie z 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

Inteligentnym zastosowaniem Contains byłoby zastąpienie wielu klauzul if w wywołaniu Contains .

Zamiast tego:

if(status == 1 || status == 3 || status == 4)
{
    //Do some business operation
}
else
{
    //Do something else
}

Zrób to:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow