Suche…


Einführung

LINQ ist eine Abkürzung für L anguage IN tegrated Q uery. Es ist ein Konzept, das eine Abfragesprache integriert, indem ein konsistentes Modell für das Arbeiten mit Daten über verschiedene Arten von Datenquellen und -formaten hinweg bereitgestellt wird. Sie verwenden dieselben grundlegenden Codierungsmuster zum Abfragen und Umwandeln von Daten in XML-Dokumenten, SQL-Datenbanken, ADO.NET-Datensätzen, .NET-Sammlungen und allen anderen Formaten, für die ein LINQ-Anbieter verfügbar ist.

Syntax

  • Abfragesyntax:

    • von <Bereichsvariable> in <Sammlung>
    • [von <Bereichsvariable> in <Sammlung>, ...]
    • <Filter, Verknüpfen, Gruppieren, Aggregatoperatoren, ...> <Lambda-Ausdruck>
    • <select oder groupBy operator> <formulieren Sie das Ergebnis>
  • Methodensyntax:

    • Enumerable.Aggregate (func)
    • Enumerable.Aggregate (Seed, func)
    • Enumerable.Aggregate (Seed, func, resultSelector)
    • Enumerable.All (Prädikat)
    • Enumerable.Any ()
    • Enumerable.Any (Prädikat)
    • Enumerable.AsEnumerable ()
    • Aufzählungszeichen. Durchschnitt ()
    • Enumerable.Average (Auswahl)
    • Enumerable.Cast <Ergebnis> ()
    • Enumerable.Concat (Sekunde)
    • Enumerable.Contains (Wert)
    • Enumerable.Contains (Wert, Vergleicher)
    • Enumerable.Count ()
    • Enumerable.Count (Prädikat)
    • Enumerable.DefaultIfEmpty ()
    • Enumerable.DefaultIfEmpty (defaultValue)
    • Enumerable.Distinct ()
    • Enumerable.Distinct (Vergleicher)
    • Enumerable.ElementAt (Index)
    • Enumerable.ElementAtOrDefault (Index)
    • Enumerable.Empty ()
    • Enumerable.Except (Sekunde)
    • Enumerable.Except (zweiter Vergleicher)
    • Enumerable.First ()
    • Enumerable.First (Prädikat)
    • Enumerable.FirstOrDefault ()
    • Enumerable.FirstOrDefault (Prädikat)
    • Enumerable.GroupBy (keySelector)
    • Enumerable.GroupBy (keySelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector)
    • Enumerable.GroupBy (keySelector, Vergleicher)
    • Enumerable.GroupBy (keySelector, resultSelector, vergleicher)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector, Vergleicher)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector, Vergleicher)
    • Enumerable.Intersect (Sekunde)
    • Enumerable.Intersect (zweiter Vergleicher)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, Vergleicher)
    • Enumerable.Last ()
    • Enumerable.Last (Prädikat)
    • Enumerable.LastOrDefault ()
    • Enumerable.LastOrDefault (Prädikat)
    • Enumerable.LongCount ()
    • Enumerable.LongCount (Prädikat)
    • Enumerable.Max ()
    • Enumerable.Max (Auswahl)
    • Enumerable.Min ()
    • Enumerable.Min (Auswahl)
    • Enumerable.OfType <TResult> ()
    • Enumerable.OrderBy (keySelector)
    • Enumerable.OrderBy (keySelector, Vergleicher)
    • Enumerable.OrderByDescending (keySelector)
    • Enumerable.OrderByDescending (keySelector, Vergleicher)
    • Enumerable.Range (Start, Anzahl)
    • Enumerable.Repeat (Element, Anzahl)
    • Enumerable.Reverse ()
    • Enumerable.Select (Selektor)
    • Enumerable.SelectMany (Selektor)
    • Enumerable.SelectMany (collectionSelector, resultSelector)
    • Enumerable.SequenceEqual (second)
    • Enumerable.SequenceEqual (zweiter Vergleicher)
    • Enumerable.Single ()
    • Enumerable.Single (Prädikat)
    • Enumerable.SingleOrDefault ()
    • Enumerable.SingleOrDefault (Prädikat)
    • Enumerable.Skip (Anzahl)
    • Enumerable.SkipWhile (Prädikat)
    • Enumerable.Sum ()
    • Enumerable.Sum (Selektor)
    • Enumerable.Take (Anzahl)
    • Enumerable.TakeWhile (Prädikat)
    • orderEnumerable.ThenBy (keySelector)
    • orderEnumerable.ThenBy (keySelector, comparer)
    • orderEnumerable.ThenByDescending (keySelector)
    • orderEnumerable.ThenByDescending (keySelector, comparer)
    • Enumerable.ToArray ()
    • Enumerable.ToDictionary (keySelector)
    • Enumerable.ToDictionary (keySelector, elementSelector)
    • Enumerable.ToDictionary (keySelector, Vergleicher)
    • Enumerable.ToDictionary (keySelector, elementSelector, comparer)
    • Enumerable.ToList ()
    • Enumerable.ToLookup (keySelector)
    • Enumerable.ToLookup (keySelector, elementSelector)
    • Enumerable.ToLookup (keySelector, Vergleicher)
    • Enumerable.ToLookup (keySelector, elementSelector, Vergleicher)
    • Enumerable.Union (Sekunde)
    • Enumerable.Union (zweiter Vergleicher)
    • Enumerable.Where (Prädikat)
    • Enumerable.Zip (zweitens resultSelector)

Bemerkungen

Um LINQ-Abfragen verwenden zu können, müssen Sie System.Linq importieren.

Die Methodensyntax ist leistungsfähiger und flexibler, aber die Abfragesyntax ist möglicherweise einfacher und bekannter. Alle in der Abfragesyntax geschriebenen Abfragen werden vom Compiler in die funktionale Syntax übersetzt. Die Leistung ist also gleich.

Abfrageobjekte werden erst ausgewertet, wenn sie verwendet werden. Sie können also ohne Leistungseinbußen geändert oder hinzugefügt werden.

Woher

Gibt eine Teilmenge von Elementen zurück, für die das angegebene Prädikat wahr ist.

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

Methodensyntax

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

Abfragesyntax

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

Wählen Sie - Elemente transformieren

Mit Select können Sie eine Transformation auf jedes Element in einer Datenstruktur anwenden, die IEnumerable implementiert.

Abrufen des ersten Zeichens jeder Zeichenfolge in der folgenden Liste:

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

Verwenden der regulären (Lambda) Syntax

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

Ausgabe:

O
B
B
E
H
M

Live-Demo zu .NET-Geige

Verwenden der LINQ-Abfragesyntax

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

Verkettungsmethoden

Viele LINQ-Funktionen arbeiten sowohl mit einem IEnumerable<TSource> als auch mit einem IEnumerable<TResult> . Die Typparameter TSource und TResult können sich abhängig von der TResult Methode und den an sie übergebenen Funktionen auf denselben Typ beziehen.

Einige Beispiele dafür sind

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
)

Bei einigen Methodenverkettungen kann es vorkommen, dass vor dem Fortfahren ein ganzer Satz bearbeitet werden muss. LINQ nutzt jedoch die verzögerte Ausführung, indem er Rendite-Return- MSDN verwendet , wodurch ein Enumerable und ein Enumerator hinter den Kulissen erstellt werden. Beim Verketten in LINQ wird im Wesentlichen ein Aufzählungszeichen (Iterator) für den ursprünglichen Satz erstellt, der zurückgestellt wird, bis er durch Aufzählung des Aufzählungszeichens verwirklicht wird .

Dadurch können diese Funktionen fließend in einem Wiki verkettet werden , wobei eine Funktion direkt auf das Ergebnis einer anderen wirken kann. Mit dieser Art von Code können viele sequenzbasierte Operationen in einer einzigen Anweisung ausgeführt werden.

Beispielsweise können Sie Select , Where und OrderBy kombinieren, um eine Sequenz in einer einzelnen Anweisung zu transformieren, zu filtern und zu sortieren.

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

Ausgabe:

2
4
8

Live-Demo zu .NET-Geige

Alle Funktionen, die den generischen IEnumerable<T> -Typ erweitern und zurückgeben, können als verkettete Klauseln in einer einzelnen Anweisung verwendet werden. Diese Art der fließenden Programmierung ist leistungsstark und sollte bei der Erstellung eigener Erweiterungsmethoden berücksichtigt werden .

Bereich und Wiederholung

Mit den statischen Methoden Range und Repeat von Enumerable können einfache Sequenzen generiert werden.

Angebot

Enumerable.Range() generiert eine Folge von Ganzzahlen mit einem Startwert und einer Zählung.

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

Live-Demo zu .NET-Geige

Wiederholen

Enumerable.Repeat() generiert eine Folge sich wiederholender Elemente, die einem Element und der Anzahl der erforderlichen Wiederholungen zugeordnet sind.

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

Live-Demo zu .NET-Geige

Überspringen und nehmen

Die Skip-Methode gibt eine Auflistung zurück, die eine Anzahl von Elementen am Anfang der Quellauflistung ausschließt. Die Anzahl der ausgeschlossenen Elemente ist die als Argument angegebene Anzahl. Wenn die Sammlung weniger Elemente enthält als im Argument angegeben, wird eine leere Sammlung zurückgegeben.

Die Take-Methode gibt eine Auflistung zurück, die eine Reihe von Elementen vom Anfang der Quellauflistung enthält. Die Anzahl der enthaltenen Elemente ist die als Argument angegebene Anzahl. Wenn die Sammlung weniger Elemente enthält als im Argument angegeben, enthält die zurückgegebene Sammlung die gleichen Elemente wie die Quellensammlung.

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 zu .NET-Geige

Skip und Take werden häufig zusammen verwendet, um Ergebnisse zu paginieren, zum Beispiel:

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

Warnung: LINQ to Entities unterstützt nur das Überspringen von bestellten Abfragen . Wenn Sie versuchen, Skip ohne Reihenfolge zu verwenden, erhalten Sie eine NotSupportedException mit der Meldung "Die Methode 'Skip' wird nur für sortierte Eingaben in LINQ to Entities unterstützt. Die Methode 'OrderBy' muss vor der Methode 'Skip' aufgerufen werden.

Zuerst FirstOrDefault, Last, LastOrDefault, Single und SingleOrDefault

Alle sechs Methoden geben einen einzelnen Wert des Sequenztyps zurück und können mit oder ohne Prädikat aufgerufen werden.

Abhängig von der Anzahl der Elemente, die dem predicate oder, wenn kein predicate ist, der Anzahl der Elemente in der Quellsequenz, verhalten sie sich wie folgt:

Zuerst()

  • Gibt das erste Element einer Sequenz oder das erste Element zurück, das dem angegebenen predicate .
  • Wenn die Sequenz keine Elemente enthält, wird eine InvalidOperationException mit der folgenden Meldung ausgelöst: "Sequenz enthält keine Elemente".
  • Wenn die Sequenz keine Elemente enthält, die mit dem bereitgestellten predicate übereinstimmen, wird eine InvalidOperationException mit der Nachricht "Sequenz enthält kein übereinstimmendes Element" ausgelöst.

Beispiel

// 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 zu .NET-Geige

FirstOrDefault ()

  • Gibt das erste Element einer Sequenz oder das erste Element zurück, das dem angegebenen predicate .
  • Wenn die Sequenz keine Elemente oder keine Elemente enthält, die dem angegebenen predicate , wird der Standardwert des Sequenztyps mit default(T) .

Beispiel

// 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 zu .NET-Geige

Zuletzt()

  • Gibt das letzte Element einer Sequenz oder das letzte Element zurück, das mit dem angegebenen predicate übereinstimmt.
  • Wenn die Sequenz keine Elemente enthält, wird eine InvalidOperationException mit der Meldung "Sequenz enthält keine Elemente" ausgelöst.
  • Wenn die Sequenz keine Elemente enthält, die mit dem bereitgestellten predicate übereinstimmen, wird eine InvalidOperationException mit der Nachricht "Sequenz enthält kein übereinstimmendes Element" ausgelöst.

Beispiel

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

  • Gibt das letzte Element einer Sequenz oder das letzte Element zurück, das mit dem angegebenen predicate übereinstimmt.
  • Wenn die Sequenz keine Elemente oder keine Elemente enthält, die dem angegebenen predicate , wird der Standardwert des Sequenztyps mit default(T) .

Beispiel

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

Single()

  • Wenn die Sequenz genau ein Element oder genau ein Element enthält, das mit dem angegebenen predicate übereinstimmt, wird dieses Element zurückgegeben.
  • Wenn die Sequenz keine Elemente enthält oder keine Elemente, die dem angegebenen predicate , wird eine InvalidOperationException mit der Nachricht "Sequenz enthält keine Elemente" ausgelöst.
  • Wenn die Sequenz mehr als ein Element oder mehr als ein Element enthält, das mit dem bereitgestellten predicate übereinstimmt, wird eine InvalidOperationException mit der Meldung "Sequenz enthält mehr als ein Element" ausgelöst.
  • Hinweis: Um auszuwerten, ob die Sequenz genau ein Element enthält, müssen höchstens zwei Elemente aufgelistet werden.

Beispiel

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

  • Wenn die Sequenz genau ein Element oder genau ein Element enthält, das mit dem angegebenen predicate übereinstimmt, wird dieses Element zurückgegeben.
  • Wenn die Sequenz keine Elemente enthält oder keine Elemente, die dem angegebenen predicate , wird default(T) zurückgegeben.
  • Wenn die Sequenz mehr als ein Element oder mehr als ein Element enthält, das mit dem bereitgestellten predicate übereinstimmt, wird eine InvalidOperationException mit der Meldung "Sequenz enthält mehr als ein Element" ausgelöst.
  • Wenn die Sequenz keine Elemente enthält, die dem bereitgestellten predicate , wird der Standardwert des Sequenztyps mit default(T) .
  • Hinweis: Um auszuwerten, ob die Sequenz genau ein Element enthält, müssen höchstens zwei Elemente aufgelistet werden.

Beispiel

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

Empfehlungen

  • Obwohl Sie FirstOrDefault , LastOrDefault oder SingleOrDefault , um zu prüfen, ob eine Sequenz Elemente enthält, sind Any oder Count zuverlässiger. Dies liegt daran, dass ein Rückgabewert von default(T) aus einer dieser drei Methoden nicht beweist, dass die Sequenz leer ist, da der Wert des ersten / letzten / einzelnen Elements der Sequenz gleichermaßen default(T)

  • Entscheiden Sie, welche Methode für Ihren Code am besten geeignet ist. Verwenden Sie beispielsweise Single nur dann, wenn Sie sicherstellen müssen, dass ein einzelnes Element in der Auflistung Ihrem Prädikat entspricht. Andernfalls verwenden Sie First . als Single eine Ausnahme, wenn die Sequenz mehr als ein übereinstimmendes Element enthält. Dies gilt natürlich auch für die "* OrDefault" -Counterparts.

  • In Bezug auf die Effizienz: Obwohl es oft angebracht ist, sicherzustellen, dass es nur ein Element ( Single ) oder entweder nur ein oder null ( SingleOrDefault ) SingleOrDefault , die von einer Abfrage zurückgegeben werden, erfordern beide Methoden mehr und oft die gesamte Sammlung der Auflistung geprüft werden, um sicherzustellen, dass keine zweite Übereinstimmung mit der Abfrage besteht. Dies unterscheidet sich beispielsweise vom Verhalten der First Methode, die nach dem Finden der ersten Übereinstimmung erfüllt werden kann.

Außer

Die Except-Methode gibt die Menge der Elemente zurück, die in der ersten Sammlung enthalten sind, aber nicht in der zweiten. Der Standard- IEqualityComparer wird verwendet, um die Elemente in den beiden Sätzen zu vergleichen. Es gibt eine Überladung, die einen IEqualityComparer als Argument akzeptiert.

Beispiel:

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

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

Ausgabe:

1
4

Live-Demo zu .NET-Geige

In diesem Fall .Except(second) schließt in dem Array enthaltenen Elemente second , nämlich 2 und 3 (0 und 5 sind nicht in der enthaltenen first Array und werden übersprungen).

Beachten Sie, dass Except impliziert Distinct (dh es werden wiederholte Elemente entfernt). Zum Beispiel:

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

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

Ausgabe:

1
4

Live-Demo zu .NET-Geige

In diesem Fall werden die Elemente 1 und 4 nur einmal zurückgegeben.


IEquatable oder die Funktion eines IEqualityComparer , können Sie die Elemente mit einer anderen Methode vergleichen. Beachten Sie, dass die GetHashCode Methode auch überschrieben werden sollte, damit ein identischer Hashcode für ein object , das gemäß der IEquatable Implementierung identisch ist.

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

Ausgabe:

Chanukka

Live-Demo zu .NET-Geige

SelectMany: Reduzieren einer Sequenz von Sequenzen

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 }

Verwenden Sie SelectMany() wenn Sie bereits eine Sequenz von Sequenzen haben oder erstellen, das Ergebnis jedoch als eine lange Sequenz erstellt werden soll.

In LINQ-Abfragesyntax:

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

Wenn Sie über eine Sammlung von Sammlungen verfügen und gleichzeitig Daten aus der übergeordneten und SelectMany Sammlung SelectMany , ist dies auch mit SelectMany möglich.

Definieren wir einfache Klassen

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

Nehmen wir an, wir haben folgende Sammlung.

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

Jetzt möchten wir Kommentare Content zusammen mit der Id von BlogPost die diesem Kommentar zugeordnet ist. Dazu können wir eine entsprechende SelectMany Überladung verwenden.

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

Unsere commentsWithIds sieht so aus

{
    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

Die SelectMany linq-Methode "flachtet" einen IEnumerable<IEnumerable<T>> IEnumerable<T> in einen IEnumerable<T> . Alle T-Elemente innerhalb der IEnumerable Instanzen, die im Quell- IEnumerable sind, werden zu einem einzigen IEnumerable .

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

Wenn Sie eine Auswahlfunktion verwenden, die Eingabeelemente in Sequenzen umwandelt, werden die Elemente dieser Sequenzen nacheinander zurückgegeben.

Beachten Sie, dass im Gegensatz zu Select() die Anzahl der Elemente in der Ausgabe nicht dieselbe sein muss wie in der Eingabe.

Mehr Praxisbeispiel

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

Ausgabe:

Bob
Jack
Jim
John

Live-Demo zu .NET-Geige

Alles

All wird verwendet, um zu überprüfen, ob alle Elemente einer Sammlung einer Bedingung entsprechen oder nicht.
siehe auch: .Any

1. Leerer Parameter

All : darf nicht mit leeren Parametern verwendet werden.

2. Lambda-Ausdruck als Parameter

All : Gibt " true wenn alle Elemente der Collection den Lambda-Ausdruck erfüllen, und " false :

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. Leere Sammlung

All : Gibt " true wenn die Auflistung leer ist und ein Lambda-Ausdruck angegeben wird:

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

Hinweis: All stoppen die Iteration der Sammlung, sobald ein Element gefunden wird, das nicht der Bedingung entspricht. Dies bedeutet, dass die Sammlung nicht unbedingt vollständig aufgezählt wird. Es wird nur so weit aufgezählt, dass der erste Artikel nicht der Bedingung entspricht.

Abfragesammlung nach Typ / Cast-Elementen zum 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 };

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

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

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 

Union

Führt zwei Sammlungen zusammen, um mithilfe des Standardgleichheitsvergleichs eine eigene Sammlung zu erstellen

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 zu .NET-Geige

VERBINDUNGEN

Joins werden verwendet, um verschiedene Listen oder Tabellen, die Daten enthalten, über einen gemeinsamen Schlüssel zu kombinieren.

Wie in SQL werden in LINQ die folgenden Arten von Joins unterstützt:
Inner, links, rechts, Flanke und Full Outer Joins.

Die folgenden zwei Listen werden in den folgenden Beispielen verwendet:

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

(Inner) Join

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

Linke äußere Verbindung

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

Rechter äußerer 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"}

Voller äußerer Join

var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);

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

Praktisches Beispiel

Die obigen Beispiele haben eine einfache Datenstruktur, sodass Sie sich darauf konzentrieren können, die verschiedenen LINQ-Joins technisch zu verstehen. In der realen Welt würden Sie jedoch Tabellen mit Spalten haben, die Sie verknüpfen müssen.

Im folgenden Beispiel wird nur eine Klasse Region verwendet. In Wirklichkeit würden Sie zwei oder mehr verschiedene Tabellen mit demselben Schlüssel verknüpfen (in diesem Beispiel werden die first und die second über die gemeinsame Schlüssel- ID ).

Beispiel: Betrachten Sie die folgende Datenstruktur:

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

Bereiten Sie nun die Daten vor (dh füllen Sie sie mit Daten auf):

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

Sie können in diesem Beispiel sehen , dass first keine Region Beschreibungen enthält , damit Sie sie aus anschließen möchten second . Dann würde der innere Join aussehen:

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

Dieses Ergebnis hat anonyme Objekte select new { f.ID, s.RegionDescription }; erstellt, was in Ordnung ist, aber wir haben bereits eine richtige Klasse erstellt - so können wir sie angeben: Anstelle von select new { f.ID, s.RegionDescription }; wir können " select new Region(f.ID, s.RegionDescription); , die dieselben Daten zurückgeben, aber Objekte vom Typ Region erstellen, wodurch die Kompatibilität mit den anderen Objekten erhalten bleibt.

Live-Demo zu .NET-Geige

Eindeutig

Gibt eindeutige Werte aus einem IEnumerable . Die Eindeutigkeit wird mit dem Standardgleichheitsvergleicher ermittelt.

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

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

Um einen benutzerdefinierten Datentyp vergleichen zu können, müssen Sie die IEquatable<T> Schnittstelle IEquatable<T> implementieren und GetHashCode und Equals Methoden für den Typ bereitstellen. Oder der Gleichheitsvergleicher kann außer Kraft gesetzt werden:

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

GroupBy ein oder mehrere Felder

Nehmen wir an, wir haben ein Filmmodell:

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

Gruppieren nach Kategorie-Eigenschaft:

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

Gruppieren nach Kategorie und Jahr:

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

Verwendung von Range mit verschiedenen Linq-Methoden

Sie können die Enumerable-Klasse zusammen mit Linq-Abfragen verwenden, um Loops in Linq-One-Liners zu konvertieren.

Wählen Sie Beispiel

Gegenteil davon:

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

Du kannst das:

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

Wo Beispiel

In diesem Beispiel werden 100 Zahlen generiert und sogar Zahlen extrahiert

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

Abfrage Bestellung - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()

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

Aufsteigend:

Abfragesyntax

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

Methodensyntax

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

SortierteNames enthält die Namen in der folgenden Reihenfolge: "adam", "mark", "steve"

Absteigend:

Abfragesyntax

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

Methodensyntax

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

SortierteNames enthält die Namen in der folgenden Reihenfolge: "Steve", "Mark", "Adam"

Nach mehreren Feldern bestellen

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

Abfragesyntax

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

Methodensyntax

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

Ergebnis

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

Grundlagen

LINQ ist für das Abfragen von Sammlungen (oder Arrays) von großem Nutzen.

Angenommen, die folgenden Beispieldaten:

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

Wir können diese Daten mithilfe der LINQ-Syntax "abfragen". So können Sie beispielsweise alle Schüler abrufen, die heute einen Snack erhalten:

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

Oder um Schüler mit einer Note von 90 oder höher abzurufen und nur ihren Namen und nicht das vollständige Student :

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

Die LINQ-Funktion besteht aus zwei Syntaxen, die die gleichen Funktionen ausführen, eine nahezu identische Leistung haben, jedoch sehr unterschiedlich geschrieben sind. Die Syntax im obigen Beispiel wird als Abfragesyntax bezeichnet . Das folgende Beispiel veranschaulicht jedoch die Methodensyntax . Die gleichen Daten werden wie im obigen Beispiel zurückgegeben. Die Abfrage ist jedoch anders.

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

Gruppiere nach

GroupBy ist eine einfache Möglichkeit, eine IEnumerable<T> -Sammlung von Elementen in verschiedene Gruppen zu sortieren.

Einfaches Beispiel

In diesem ersten Beispiel erhalten wir zwei Gruppen, ungerade und gerade Elemente.

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

Komplexeres Beispiel

Nehmen wir als Beispiel eine Liste von Personen nach Alter. Zuerst erstellen wir ein Personenobjekt mit zwei Eigenschaften, Name und Alter.

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

Dann erstellen wir eine Musterliste von Personen mit verschiedenen Namen und Alter.

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

Dann erstellen wir eine LINQ-Abfrage, um die Liste der Personen nach Alter zu gruppieren.

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

Auf diese Weise können wir das Alter für jede Gruppe sehen und eine Liste aller Personen in der Gruppe haben.

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

Daraus ergibt sich folgende Ausgabe:

20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith

Sie können mit der Live-Demo auf .NET Fiddle spielen

Irgendein

Any wird geprüft, ob ein Element einer Collection einer Bedingung entspricht oder nicht.
Siehe auch: .All , Any und FirstOrDefault: Best Practice

1. Leerer Parameter

Any : Gibt true wenn die Collection Elemente enthält, und false wenn die Collection leer ist:

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. Lambda-Ausdruck als Parameter

Any : Gibt true wenn die Auflistung ein oder mehrere Elemente enthält, die die Bedingung im Lambda-Ausdruck erfüllen:

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

3. Leere Sammlung

Any : Gibt false wenn die Sammlung leer ist und ein Lambda-Ausdruck angegeben wird:

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

Hinweis: Any stoppt die Iteration der Sammlung, sobald ein der Bedingung entsprechendes Element gefunden wird. Dies bedeutet, dass die Sammlung nicht unbedingt vollständig aufgezählt wird. Es wird nur so weit aufgezählt, dass der erste Artikel gefunden wird, der der Bedingung entspricht.

Live-Demo zu .NET-Geige

ToDictionary

Mit der ToDictionary() LINQ-Methode kann eine Dictionary<TKey, TElement> -Sammlung basierend auf einer bestimmten IEnumerable<T> .

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

In diesem Beispiel hat das einzige an ToDictionary Argument den Typ Func<TSource, TKey> , der den Schlüssel für jedes Element zurückgibt.

Dies ist eine prägnante Möglichkeit, die folgende Operation auszuführen:

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

Sie können auch einen zweiten Parameter an die ToDictionary Methode übergeben, der vom Typ Func<TSource, TElement> und den für jeden Eintrag hinzuzufügenden Value zurückgibt.

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

Es ist auch möglich, den IComparer anzugeben, der zum Vergleich von Schlüsselwerten verwendet wird. Dies kann nützlich sein, wenn der Schlüssel eine Zeichenfolge ist und die Groß- / Kleinschreibung berücksichtigt werden soll.

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

Hinweis: Für die ToDictionary Methode müssen alle Schlüssel eindeutig sein. Es dürfen keine doppelten Schlüssel vorhanden sein. Wenn ArgumentException: An item with the same key has already been added. , wird eine Ausnahme ausgelöst: ArgumentException: An item with the same key has already been added. Wenn Sie ein Szenario haben, in dem Sie wissen, dass Sie mehrere Elemente mit demselben Schlüssel haben werden, sollten Sie lieber ToLookup verwenden.

Aggregat

Aggregate Wendet eine Akkumulatorfunktion auf eine Sequenz an.

int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
  • Im ersten Schritt ist prevSum = 1
  • Am zweiten prevSum = prevSum(at the first step) + 2
  • Im i-ten Schritt 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 !"

Bei einer zweiten Überladung von Aggregate auch ein seed Parameter empfangen, der der anfängliche Akkumulatorwert ist. Dies kann verwendet werden, um mehrere Bedingungen für eine Sammlung zu berechnen, ohne sie mehrmals zu durchlaufen.

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

Für die Sammlung von items wollen wir berechnen

  1. Die Summe .Count
  2. Die Anzahl der geraden Zahlen
  3. Sammle jeden weiteren Gegenstand ein

Mit Aggregate kann man so vorgehen:

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]

Beachten Sie, dass bei Verwendung eines anonymen Typs als Startwert ein neues Objekt für jedes Element instanziiert werden muss, da die Eigenschaften schreibgeschützt sind. Mit einer benutzerdefinierten Klasse kann man die Informationen einfach zuweisen und es ist keine new erforderlich (nur wenn der anfängliche seed angegeben wird)

Definieren einer Variablen in einer Linq-Abfrage (Schlüsselwort let)

Um eine Variable innerhalb eines linq-Ausdrucks zu definieren, können Sie das let- Schlüsselwort verwenden. Dies geschieht normalerweise, um die Ergebnisse von Zwischenabfragen zu speichern, zum Beispiel:

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

Ausgabe:

Der Durchschnitt der Zahlen ist 4,5.
Das Abfrageergebnis enthält die Nummer 3 mit dem Quadrat von 9.
Das Abfrageergebnis enthält die Nummer 4 mit dem Quadrat von 16.
Das Abfrageergebnis enthält die Nummer 5 mit dem Quadrat von 25.
Das Abfrageergebnis enthält die Nummer 6 mit dem Quadrat von 36.
Das Abfrageergebnis enthält die Nummer 7 mit dem Quadrat von 49.
Das Abfrageergebnis enthält die Nummer 8 mit einem Quadrat von 64.
Das Abfrageergebnis enthält die Nummer 9 mit dem Quadrat von 81.

Demo anzeigen

SkipWährend

SkipWhile() wird verwendet, um Elemente bis zur ersten Nichtübereinstimmung auszuschließen.

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

DefaultIfEmpty

DefaultIfEmpty wird verwendet, um ein Standardelement zurückzugeben, wenn die Sequenz keine Elemente enthält. Dieses Element kann der Standard des Typs oder eine benutzerdefinierte Instanz dieses Typs sein. Beispiel:

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;

Verwendung in Left Joins :

Mit DefaultIfEmpty der herkömmliche Linq-Join ein Standardobjekt zurückgeben, wenn keine Übereinstimmung gefunden wurde. So fungiert sie als linker Join von SQL. Beispiel:

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

DefaultIfEmpty ein DefaultIfEmpty verwendet wird (ohne einen Standardwert anzugeben) und dies zu keinen übereinstimmenden Elementen in der richtigen Sequenz führt, müssen Sie sicherstellen, dass das Objekt nicht null bevor Sie auf seine Eigenschaften zugreifen. Andernfalls führt dies zu einer NullReferenceException . Beispiel:

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 wird verwendet, um zwei IEnumerable<T> -Sequenzen miteinander zu vergleichen.

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 und LongCount

Count gibt die Anzahl der Elemente in einem IEnumerable<T> . Count stellt auch einen optionalen Prädikatparameter bereit, mit dem Sie die Elemente filtern können, die Sie zählen möchten.

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 funktioniert genauso wie Count , hat jedoch den Rückgabetyp long und wird zum Zählen von IEnumerable<T> -Sequenzen verwendet, die länger als 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

Inkrementelles Erstellen einer Abfrage

Da LINQ die verzögerte Ausführung verwendet , können wir ein Abfrageobjekt haben, das die Werte nicht tatsächlich enthält, aber bei der Auswertung die Werte zurückgibt. Auf diese Weise können wir die Abfrage basierend auf unserem Kontrollfluss dynamisch erstellen und auswerten, sobald wir fertig sind:

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

Wir können Filter bedingt anwenden:

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

Wir können der Abfrage eine Sortierreihenfolge basierend auf einer Bedingung hinzufügen:

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

Unsere Abfrage kann so definiert werden, dass sie an einem bestimmten Punkt beginnt:

    query = query.Skip(start - 1);

und definiert, um eine bestimmte Anzahl von Datensätzen zurückzugeben:

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

Sobald wir das Abfrageobjekt haben, können wir die Ergebnisse mit einer foreach Schleife oder einer der LINQ-Methoden auswerten, die eine Menge von Werten wie ToList oder ToArray :

SearchModel sm;

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

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

Postleitzahl

Die Zip Erweiterungsmethode wirkt auf zwei Sammlungen. Es paart jedes Element in den beiden Reihen nach Position. Bei einer Func Instanz verwenden wir Zip , um Elemente aus den beiden C # -Sammlungen paarweise zu behandeln. Wenn sich die Serien in der Größe unterscheiden, werden die zusätzlichen Elemente der größeren Serie ignoriert.

Um ein Beispiel aus dem Buch "C # in a Nutshell" zu nehmen,

int[] numbers = { 3, 5, 7 };
string[] words = { "three", "five", "seven", "ignored" };
IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);

Ausgabe:

3 = drei
5 = fünf
7 = sieben

Demo anzeigen

GroupJoin mit Variable für den äußeren Bereich

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

ElementAt gibt das Element am Index n . Wenn sich n nicht innerhalb des Aufzählungsbereichs befindet, wird eine ArgumentOutOfRangeException .

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

ElementAtOrDefault gibt das Element an Index n . Wenn n nicht innerhalb des Aufzählungsbereichs liegt, wird ein default(T) .

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

Sowohl ElementAt als auch ElementAtOrDefault sind optimiert, wenn die Quelle eine IList<T> und in diesen Fällen die normale Indizierung verwendet wird.

Beachten Sie, dass für ElementAt , wenn der bereitgestellte Index größer als IList<T> , die Liste eine ArgumentOutOfRangeException IList<T> soll (technisch gesehen jedoch nicht garantiert).

Linq-Quantifizierer

Quantifiziereroperationen geben einen booleschen Wert zurück, wenn einige oder alle Elemente in einer Sequenz eine Bedingung erfüllen. In diesem Artikel werden einige allgemeine LINQ to Objects-Szenarien beschrieben, in denen wir diese Operatoren verwenden können. Es gibt drei Quantifizierervorgänge, die in LINQ verwendet werden können:

All - Wird verwendet, um zu bestimmen, ob alle Elemente einer Sequenz eine Bedingung erfüllen. Z.B:

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 - Any verwendet, um zu bestimmen, ob Elemente in einer Sequenz eine Bedingung erfüllen. Z.B:

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

Contains - Bestimmt, ob eine Sequenz ein bestimmtes Element enthält. Z.B:

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

Mehrere Sequenzen verbinden

Betrachten Sie die Entitäten Customer , Purchase und PurchaseItem wie folgt:

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

Betrachten Sie die folgenden Beispieldaten für die obigen Entitäten:

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

Betrachten Sie nun die folgende linq-Abfrage:

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

So geben Sie das Ergebnis der obigen Abfrage aus:

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

Die Ausgabe der Abfrage wäre:

Kunde1, Kunde1-Kauf1, Kauf1-PurchaseItem1

Kunde1, Kunde1-Einkauf2, Einkauf2-Einkaufselement1

Kunde1, Kunde1-Kauf2, Kauf2-PurchaseItem2

Customer2, Customer2-Purchase2, Purchase3-PurchaseItem1

Live-Demo zu .NET-Geige

Zusammenfügen auf mehreren Schlüsseln

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

Beachten Sie, dass anonyme Typen in der obigen join dieselben Eigenschaften enthalten müssen, da Objekte nur dann als gleich betrachtet werden, wenn alle ihre Eigenschaften gleich sind. Andernfalls wird die Abfrage nicht kompiliert.

Wählen Sie mit Func Selector - Verwenden Sie diese Option, um die Rangfolge der Elemente abzurufen

Auf den Überlastungen der Select Erweiterungsmethoden führt auch den index des aktuellen Elements in der Sammlung seines select hrsg. Dies sind einige Anwendungen davon.

Holen Sie sich die "Zeilennummer" der Artikel

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

Liefert den Rang eines Gegenstandes innerhalb seiner Gruppe

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

Rufen Sie das Ranking von Gruppen ab (auch in Oracle als dense_rank bekannt).

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

Zum Testen können Sie Folgendes verwenden:

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

Und Daten:

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 gibt Elemente aus einer Sequenz zurück, solange die Bedingung erfüllt ist

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

Summe

Die Erweiterungsmethode Enumerable.Sum berechnet die Summe der numerischen Werte.

Falls die Elemente der Sammlung selbst Zahlen sind, können Sie die Summe direkt berechnen.

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

Wenn der Typ der Elemente ein komplexer Typ ist, können Sie einen Lambda-Ausdruck verwenden, um den Wert anzugeben, der berechnet werden soll:

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

Die Summenerweiterungsmethode kann mit den folgenden Typen berechnen:

  • Int32
  • Int64
  • Single
  • Doppelt
  • Dezimal

Wenn Ihre Sammlung nullfähige Typen enthält, können Sie den Nullkoaleszenzoperator verwenden, um einen Standardwert für Nullelemente festzulegen:

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

Nachschlagen

ToLookup gibt eine Datenstruktur zurück, die die Indizierung ermöglicht. Es ist eine Erweiterungsmethode. Sie erzeugt eine ILookup-Instanz, die mit einer foreach-Schleife indiziert oder aufgezählt werden kann. Die Einträge werden bei jeder Taste zu Gruppierungen zusammengefasst. - 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

Ein anderes Beispiel:

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

Erstellen Sie Ihre eigenen Linq-Operatoren für IEnumerable

Eines der großen Dinge an Linq ist, dass es so einfach zu erweitern ist. Sie müssen lediglich eine Erweiterungsmethode erstellen , deren Argument 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;
        }
    }
}

In diesem Beispiel werden die Elemente in einem IEnumerable<T> in Listen fester Größe aufgeteilt. Die letzte Liste enthält den Rest der Elemente. Beachten Sie, wie das Objekt , auf dem das Verlängerungsverfahren angewendet wird , wird in (Argument übergeben source ) als das anfängliche Argument die Verwendung this Schlüsselwort. Dann wird das yield Schlüsselwort verwendet, um das nächste Element im Ausgabe- IEnumerable<T> bevor die Ausführung ab diesem Punkt fortgesetzt wird (siehe Yield-Schlüsselwort ).

Dieses Beispiel würde in Ihrem Code folgendermaßen verwendet:

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

In der ersten Schleife wäre die Unterliste {2, 3, 4} und in der zweiten {5, 6} .

Benutzerdefinierte LinQ-Methoden können auch mit Standard-LinQ-Methoden kombiniert werden. z.B:

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

Diese Abfrage gibt gerade Zahlen zurück, die in Gruppen mit einer Größe von 3 gruppiert sind: {0, 2, 4}, {6, 8, 10}, {12}

Denken Sie daran, dass Sie using MyNamespace; Zeile, um auf die Erweiterungsmethode zugreifen zu können.

Verwenden von SelectMany anstelle von verschachtelten Schleifen

Gegeben 2 Listen

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

Wenn Sie alle Permutationen ausgeben möchten, können Sie verschachtelte Schleifen wie verwenden

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

Mit SelectMany können Sie dieselbe Operation wie ausführen

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

Any and First (OrDefault) - Best Practice

Ich werde nicht erklären, was Any und FirstOrDefault , da es bereits zwei gute Beispiele dafür gibt. Weitere Informationen finden Sie unter Any und First, FirstOrDefault, Last, LastOrDefault, Single und SingleOrDefault .

Ein Muster, das ich oft im Code sehe, sollte vermieden werden

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

Es könnte so effizienter geschrieben werden

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

Wenn Sie das zweite Beispiel verwenden, wird die Sammlung nur einmal durchsucht und ergibt dasselbe Ergebnis wie das erste. Die gleiche Idee kann auf Single angewendet werden.

GroupBy-Summe und Anzahl

Nehmen wir eine Probestunde an:

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

Lassen Sie uns nun eine Liste von Transaktionen betrachten:

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

Wenn Sie die kategoriebezogene Summe aus Betrag und Anzahl berechnen möchten, können Sie GroupBy folgendermaßen verwenden:

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

Alternativ können Sie dies in einem Schritt tun:

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

Die Ausgabe für beide obigen Abfragen wäre gleich:

Kategorie: Konto speichern, Betrag: 66, Anzahl: 2

Kategorie: Kreditkarte, Betrag: 71, Anzahl: 2

Kategorie: Laufendes Konto, Anzahl: 100, Anzahl: 1

Live-Demo in .NET-Geige

Umkehren

  • Kehrt die Reihenfolge der Elemente in einer Reihenfolge um.
  • Wenn keine Elemente vorhanden sind, wird eine ArgumentNullException: source is null.

Beispiel:

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

Live-Code-Beispiel

Beachten Sie, dass Reverse() abhängig von der Kettenreihenfolge Ihrer LINQ-Anweisungen möglicherweise anders arbeitet.

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

Live-Code-Beispiel

Reverse () funktioniert, indem es alles puffert und dann rückwärts durchläuft, was nicht sehr effizient ist, aber OrderBy ist diesbezüglich auch nicht.

In LINQ-to-Objects gibt es Pufferoperationen (Reverse, OrderBy, GroupBy usw.) und Nichtpufferoperationen (Where, Take, Skip usw.).

Beispiel: Nichtpufferung Rückwärtsverlängerung

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

Live-Code-Beispiel

Diese Methode kann auf Probleme stoßen, wenn Sie die Liste während der Iteration ändern.

Aufzählung der Aufzählungszeichen

Die IEnumerable <T> -Schnittstelle ist die Basisschnittstelle für alle generischen Enumeratoren und ein wesentlicher Bestandteil des Verständnisses von LINQ. Im Kern repräsentiert es die Sequenz.

Diese zugrunde liegende Schnittstelle wird von allen generischen Sammlungen vererbt, z. B. Collection <T> , Array , List <T> , Dictionary <TKey, TValue> -Klasse und HashSet <T> .

Zusätzlich zur Darstellung der Sequenz muss jede Klasse, die von IEnumerable <T> erbt, einen IEnumerator <T> bereitstellen. Der Enumerator macht den Iterator für das Enumerable verfügbar, und diese beiden miteinander verbundenen Schnittstellen und Ideen sind die Quelle für das Sprichwort "Enumerate the Enumerable".

"Aufzählen des Aufzählers" ist ein wichtiger Satz. Das Aufzählbare ist einfach eine Struktur für die Wiederholung, es enthält keine materialisierten Objekte. Bei der Sortierung kann ein Aufzählungszeichen beispielsweise die Kriterien des Felds für die Sortierung enthalten. .OrderBy() jedoch .OrderBy() , wird ein IEnumerable <T> zurückgegeben, das nur wissen kann, wie es sortiert werden soll. Die Verwendung eines Aufrufs, der die Objekte materialisiert, wie beim Durchlaufen der Menge, wird als Aufzählung bezeichnet (z. B. .ToList() ). Der Aufzählungsprozess verwendet die aufzählbare Definition, wie die Reihe durchlaufen werden soll und die relevanten Objekte (in der Reihenfolge gefiltert, projiziert usw.) zurückgegeben werden.

Erst wenn das Aufzählungszeichen aufgezählt wurde, führt dies zur Materialisierung der Objekte. Dies ist der Zeitpunkt, zu dem Metriken wie Zeitkomplexität (wie lange sie in Bezug auf die Seriengröße benötigt wird) und räumliche Komplexität (wie viel Platz in Bezug auf die Seriengröße beansprucht wird) können gemessen werden.

Das Erstellen einer eigenen Klasse, die von IEnumerable <T> erbt, kann etwas kompliziert sein, abhängig von der zugrunde liegenden Reihe, die aufgezählt werden muss. Im Allgemeinen ist es am besten, eine der vorhandenen generischen Sammlungen zu verwenden. Das heißt, es ist auch möglich, von der IEnumerable <T> -Schnittstelle zu erben, ohne ein definiertes Array als zugrunde liegende Struktur zu haben.

Verwenden Sie beispielsweise die Fibonacci-Serie als zugrunde liegende Sequenz. Beachten Sie, dass mit dem Aufruf von Where einfach ein IEnumerable wird. Erst wenn ein Aufruf zur IEnumerable der Aufzählungszeichen erfolgt, werden alle Werte IEnumerable .

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

Ausgabe

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

Die Stärke des zweiten Satzes (des fibMod612) ist, dass, obwohl wir den Aufruf zur Bestellung unseres gesamten Satzes von Fibonacci-Zahlen gemacht haben, da nur ein Wert mit .First() die Zeitkomplexität O (n) als nur 1 Wert war musste während der Ausführung des Bestellalgorithmus verglichen werden. Dies liegt daran, dass unser Enumerator nur nach einem Wert gefragt hat und die gesamte Aufzählung nicht materialisiert werden musste. Hätten wir .Take(5) anstelle von .First() der Enumerator nach 5 Werten gefragt, und höchstens 5 Werte müssten .First() werden. Im Vergleich zur Notwendigkeit, einen ganzen Satz zu bestellen und dann die ersten 5 Werte zu übernehmen, spart das Prinzip viel Ausführungszeit und -raum.

Sortieren nach

Bestellt eine Sammlung mit einem angegebenen Wert.

Wenn der Wert eine Ganzzahl , ein Doppel- oder ein Gleitkommawert ist , beginnt er mit dem Minimalwert , was bedeutet, dass Sie zuerst die negativen Werte und dann die Nullwerte und danach die positiven Werte erhalten (siehe Beispiel 1).

Wenn man von einem char Um das Verfahren vergleicht den ASCII - Wert der Zeichen , die Sammlung zu sortieren (siehe Beispiel 2).

Wenn Sie Zeichenfolgen sortieren, vergleicht die OrderBy-Methode sie, indem Sie deren CultureInfo- Informationen betrachten, die jedoch normalerweise mit dem ersten Buchstaben des Alphabets beginnen (a, b, c ...).

Diese Art von Reihenfolge wird als aufsteigend bezeichnet, wenn Sie es andersherum wünschen, müssen Sie absteigend (siehe OrderByDescending).

Beispiel 1:

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

Beispiel 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', '{' }

Beispiel:

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

Bestellt eine Sammlung mit einem angegebenen Wert.

Wenn der Wert eine Ganzzahl , ein Doppel- oder ein Gleitkommawert ist , beginnt er mit dem Maximalwert , was bedeutet, dass Sie zuerst die positiven Werte und dann die Nullwerte und danach die negativen Werte erhalten.

Wenn man von einem char Um das Verfahren vergleicht den ASCII - Wert der Zeichen , die Sammlung zu sortieren (siehe Beispiel 2).

Wenn Sie Strings sortieren, vergleicht die OrderBy-Methode sie, indem Sie deren CultureInfo- Informationen betrachten , die normalerweise mit dem letzten Buchstaben des Alphabets beginnen (z, y, x, ...).

Diese Art von Reihenfolge wird als absteigend bezeichnet, wenn Sie es andersherum wünschen, müssen Sie aufsteigend (siehe OrderBy).

Beispiel 1:

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

Beispiel 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', '+', '!', ' ' }

Beispiel 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

Führt zwei Sammlungen zusammen (ohne Duplikate zu entfernen)

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

Enthält

MSDN:

Bestimmt anhand eines angegebenen IEqualityComparer<T> ob eine Sequenz ein bestimmtes Element enthält

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

Verwenden eines benutzerdefinierten Objekts:

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

Verwenden der 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

Eine intelligente Verwendung von Contains darin, mehrere if Klauseln für einen Contains Aufruf zu ersetzen.

Also statt dies zu tun:

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

Mach das:

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow