Zoeken…


Invoering

LINQ is een acroniem dat staat voor L anguage IN tegrated Q uery. Het is een concept dat een zoektaal integreert door een consistent model te bieden voor het werken met gegevens in verschillende soorten gegevensbronnen en -indelingen; u gebruikt dezelfde basiscoderingspatronen om gegevens in XML-documenten, SQL-databases, ADO.NET-gegevenssets, .NET-verzamelingen en elk ander formaat waarvoor een LINQ-provider beschikbaar is, op te vragen en te transformeren.

Syntaxis

  • Syntaxis van zoekopdracht:

    • van <bereik variabele> in <collectie>
    • [uit <bereikvariabele> in <collectie>, ...]
    • <filteren, samenvoegen, groeperen, aggregaten, ...> <lambda-expressie>
    • <selecteer of groep Door operator> <formuleer het resultaat>
  • Methode syntaxis:

    • Enumerable.Aggregate (func)
    • Opsombaar. Samenvoegen (zaad, func)
    • Enumerable.Aggregate (seed, func, resultSelector)
    • Enumerable.All (predikaat)
    • Enumerable.Any ()
    • Enumerable.Any (predikaat)
    • Enumerable.AsEnumerable ()
    • Enumerable.Average ()
    • Enumerable.Average (selector)
    • Enumerable.Cast <Resultaten> ()
    • Enumerable.Concat (tweede)
    • Enumerable.Contains (value)
    • Enumerable.Contains (value, comparer)
    • Enumerable.Count ()
    • Enumerable.Count (predikaat)
    • Enumerable.DefaultIfEmpty ()
    • Enumerable.DefaultIfEmpty (defaultValue)
    • Enumerable.Distinct ()
    • Enumerable.Distinct (comparator)
    • Enumerable.ElementAt (index)
    • Enumerable.ElementAtOrDefault (index)
    • Enumerable.Empty ()
    • Enumerable.Except (tweede)
    • Enumerable.Except (tweede, vergelijker)
    • Enumerable.First ()
    • Enumerable.First (predikaat)
    • Enumerable.FirstOrDefault ()
    • Enumerable.FirstOrDefault (predikaat)
    • Enumerable.GroupBy (keySelector)
    • Enumerable.GroupBy (keySelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector)
    • Enumerable.GroupBy (keySelector, comparer)
    • Enumerable.GroupBy (keySelector, resultSelector, comparer)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector, comparer)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector, comparer)
    • Enumerable.Intersect (tweede)
    • Enumerable.Intersect (tweede, vergelijker)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, comparer)
    • Enumerable.Last ()
    • Enumerable.Last (predikaat)
    • Enumerable.LastOrDefault ()
    • Enumerable.LastOrDefault (predikaat)
    • Enumerable.LongCount ()
    • Enumerable.LongCount (predikaat)
    • Enumerable.Max ()
    • Enumerable.Max (selector)
    • Enumerable.Min ()
    • Enumerable.Min (selector)
    • Enumerable.OfType <TResult> ()
    • Enumerable.OrderBy (keySelector)
    • Enumerable.OrderBy (keySelector, comparer)
    • Enumerable.OrderByDescending (keySelector)
    • Enumerable.OrderByDescending (keySelector, comparer)
    • Enumerable.Range (start, count)
    • Enumerable.Repeat (element, count)
    • Enumerable.Reverse ()
    • Enumerable.Select (selector)
    • Enumerable.SelectMany (selector)
    • Enumerable.SelectMany (collectionSelector, resultSelector)
    • Enumerable.SequenceEqual (tweede)
    • Enumerable.SequenceEqual (tweede, vergelijker)
    • Enumerable.Single ()
    • Enumerable.Single (predikaat)
    • Enumerable.SingleOrDefault ()
    • Enumerable.SingleOrDefault (predikaat)
    • Enumerable.Skip (count)
    • Enumerable.SkipWhile (predikaat)
    • Enumerable.Sum ()
    • Enumerable.Sum (selector)
    • Enumerable.Take (count)
    • Enumerable.TakeWhile (predikaat)
    • orderedEnumerable.ThenBy (keySelector)
    • besteldEnumerable.ThenBy (keySelector, comparer)
    • orderedEnumerable.ThenByDescending (keySelector)
    • orderEnumerable.ThenByDescending (keySelector, comparer)
    • Enumerable.ToArray ()
    • Enumerable.ToDictionary (keySelector)
    • Enumerable.ToDictionary (keySelector, elementSelector)
    • Enumerable.ToDictionary (keySelector, comparer)
    • Enumerable.ToDictionary (keySelector, elementSelector, comparer)
    • Enumerable.ToList ()
    • Enumerable.ToLookup (keySelector)
    • Enumerable.ToLookup (keySelector, elementSelector)
    • Enumerable.ToLookup (keySelector, comparer)
    • Enumerable.ToLookup (keySelector, elementSelector, comparer)
    • Enumerable.Union (tweede)
    • Enumerable.Union (tweede, vergelijker)
    • Enumerable.Where (predikaat)
    • Enumerable.Zip (tweede, resultSelector)

Opmerkingen

Als u LINQ-query's wilt gebruiken, moet u System.Linq importeren.

De methode-syntaxis is krachtiger en flexibeler, maar de query-syntaxis is misschien eenvoudiger en meer vertrouwd. Alle query's die in de syntaxis van de query zijn geschreven, worden door de compiler in de functionele syntaxis vertaald, dus de prestaties zijn hetzelfde.

Query-objecten worden niet geëvalueerd totdat ze worden gebruikt, dus ze kunnen zonder prestatieverlies worden gewijzigd of aangevuld.

Waar

Retourneert een subset van items waarvoor het opgegeven predicaat waar is.

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

Methode syntaxis

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

Syntaxis van zoekopdracht

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

Selecteer - Elementen transformeren

Met Select kunt u een transformatie toepassen op elk element in elke gegevensstructuur die IEnumerable implementeert.

Het eerste karakter van elke string in de volgende lijst krijgen:

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

Gebruik van reguliere (lambda) syntaxis

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

Output:

O
B
B
E
H
M

Live demo op .NET Fiddle

LINQ Query Syntax gebruiken

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

Chaining methoden

Veel LINQ-functies werken beide op een IEnumerable<TSource> en retourneren ook een IEnumerable<TResult> . De TSource en TResult kunnen al dan niet naar hetzelfde type verwijzen, afhankelijk van de methode in kwestie en eventuele functies die eraan worden doorgegeven.

Een paar voorbeelden hiervan zijn

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
)

Hoewel voor sommige methodeketting een volledige set moet worden bewerkt voordat wordt doorgegaan, profiteert LINQ van uitgestelde uitvoering door MSDN met opbrengstteruggave te gebruiken die achter de schermen een Enumerable en een Enumerator creëert. Het proces van ketenvorming in LINQ is in wezen het bouwen van een opsomming (iterator) voor de originele set - die wordt uitgesteld - totdat deze wordt gematerialiseerd door de opsomming op te sommen .

Hierdoor kunnen deze functies vloeiend worden gekoppeld wiki , waarbij de ene functie direct kan werken op het resultaat van de andere. Deze codestijl kan worden gebruikt om veel op een reeks gebaseerde bewerkingen in één instructie uit te voeren.

Het is bijvoorbeeld mogelijk om Select , Where en OrderBy te combineren om een reeks in een enkele instructie te transformeren, filteren en sorteren.

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

Output:

2
4
8

Live demo op .NET Fiddle

Alle functies die het generieke type IEnumerable<T> zowel uitbreiden als retourneren, kunnen als kettingclausules in een enkele instructie worden gebruikt. Deze stijl van vloeiend programmeren is krachtig en moet worden overwogen bij het maken van uw eigen uitbreidingsmethoden .

Bereik en herhaal

De statische methoden Range en Repeat op Enumerable kunnen worden gebruikt om eenvoudige reeksen te genereren.

reeks

Enumerable.Range() genereert een reeks gehele getallen met een startwaarde en een telling.

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

Live demo op .NET Fiddle

Herhaling

Enumerable.Repeat() genereert een reeks herhalende elementen met een element en het vereiste aantal herhalingen.

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

Live demo op .NET Fiddle

Overslaan en nemen

De Skip-methode retourneert een verzameling exclusief een aantal items vanaf het begin van de bronverzameling. Het aantal uitgesloten items is het nummer dat als argument wordt gegeven. Als de verzameling minder items bevat dan is opgegeven in het argument, wordt een lege verzameling geretourneerd.

De methode Take retourneert een verzameling met een aantal elementen vanaf het begin van de bronverzameling. Het aantal opgenomen items is het nummer dat als argument wordt gegeven. Als er minder items in de collectie zijn dan opgegeven in het argument, bevat de geretourneerde collectie dezelfde elementen als de broncollectie.

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 op .NET Fiddle

Skip en Take worden vaak samen gebruikt om resultaten te pagineren, bijvoorbeeld:

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

Waarschuwing: LINQ to Entities ondersteunt alleen Overslaan bij bestelde zoekopdrachten . Als u Skip probeert te gebruiken zonder te bestellen, krijgt u een NotSupportedException met het bericht 'De methode' Skip 'wordt alleen ondersteund voor gesorteerde invoer in LINQ naar entiteiten. De methode' OrderBy 'moet worden aangeroepen vóór de methode' Skip '. "

Ten eerste, FirstOrDefault, Last, LastOrDefault, Single en SingleOrDefault

Alle zes methoden retourneren een enkele waarde van het sequentietype en kunnen worden opgeroepen met of zonder een predicaat.

Afhankelijk van het aantal elementen dat overeenkomt met het predicate of, als er geen predicate is opgegeven, het aantal elementen in de bronreeks, gedragen ze zich als volgt:

Eerste()

  • Retourneert het eerste element van een reeks of het eerste element dat overeenkomt met het opgegeven predicate .
  • Als de reeks geen elementen bevat, wordt een InvalidOperationException gegenereerd met het bericht: "Reeks bevat geen elementen".
  • Als de reeks geen elementen bevat die overeenkomen met het opgegeven predicate , wordt een InvalidOperationException gegenereerd met het bericht "Reeks bevat geen overeenkomend element".

Voorbeeld

// 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 op .NET Fiddle

FirstOrDefault ()

  • Retourneert het eerste element van een reeks of het eerste element dat overeenkomt met het opgegeven predicate .
  • Als de reeks geen elementen bevat, of geen elementen die overeenkomen met het opgegeven predicate , retourneert de standaardwaarde van het reekstype met default(T) .

Voorbeeld

// 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 op .NET Fiddle

Laatste()

  • Retourneert het laatste element van een reeks of het laatste element dat overeenkomt met het opgegeven predicate .
  • Als de reeks geen elementen bevat, wordt een InvalidOperationException gegenereerd met het bericht "Reeks bevat geen elementen."
  • Als de reeks geen elementen bevat die overeenkomen met het opgegeven predicate , wordt een InvalidOperationException gegenereerd met het bericht "Reeks bevat geen overeenkomend element".

Voorbeeld

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

  • Retourneert het laatste element van een reeks, of het laatste element dat overeenkomt met het opgegeven predicate .
  • Als de reeks geen elementen bevat, of geen elementen die overeenkomen met het opgegeven predicate , retourneert de standaardwaarde van het reekstype met default(T) .

Voorbeeld

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

  • Als de reeks precies één element bevat, of precies één element dat overeenkomt met het opgegeven predicate , wordt dat element geretourneerd.
  • Als de reeks geen elementen bevat of geen elementen die overeenkomen met het opgegeven predicate , wordt een InvalidOperationException gegenereerd met het bericht "Reeks bevat geen elementen".
  • Als de reeks meer dan één element bevat, of meer dan één element dat overeenkomt met het opgegeven predicate , wordt een InvalidOperationException gegenereerd met het bericht "Reeks bevat meer dan één element".
  • Opmerking: om te evalueren of de reeks precies één element bevat, moeten maximaal twee elementen worden opgesomd.

Voorbeeld

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

  • Als de reeks precies één element bevat, of precies één element dat overeenkomt met het opgegeven predicate , wordt dat element geretourneerd.
  • Als de reeks geen elementen bevat of geen elementen die overeenkomen met het opgegeven predicate , wordt default(T) geretourneerd.
  • Als de reeks meer dan één element bevat, of meer dan één element dat overeenkomt met het opgegeven predicate , wordt een InvalidOperationException gegenereerd met het bericht "Reeks bevat meer dan één element".
  • Als de reeks geen elementen bevat die overeenkomen met het opgegeven predicate , retourneert de standaardwaarde van het reekstype met default(T) .
  • Opmerking: om te evalueren of de reeks precies één element bevat, moeten maximaal twee elementen worden opgesomd.

Voorbeeld

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

aanbevelingen

  • Hoewel u FirstOrDefault , LastOrDefault of SingleOrDefault kunt gebruiken om te controleren of een reeks items bevat, zijn Any of Count betrouwbaarder. Dit komt omdat een retourwaarde van default(T) van een van deze drie methoden niet bewijst dat de reeks leeg is, omdat de waarde van het eerste / laatste / enkele element van de reeks evengoed default(T)

  • Bepaal welke methoden het meest geschikt zijn voor het doel van uw code. Gebruik bijvoorbeeld Single alleen als u ervoor moet zorgen dat er een item in de collectie is dat overeenkomt met uw predikaat - gebruik anders First ; als Single gooi een uitzondering als de reeks meer dan één overeenkomend element heeft. Dit geldt natuurlijk ook voor de "* OrDefault" -tegenpartijen.

  • Met betrekking tot efficiëntie: hoewel het vaak aangewezen is om ervoor te zorgen dat er slechts één item ( Single ) of, of slechts één of nul ( SingleOrDefault ) items is, geretourneerd door een query, vereisen beide methoden meer, en vaak de hele, van de collectie worden onderzocht om er zeker van te zijn dat er geen tweede overeenkomst is met de zoekopdracht. Dit is anders dan het gedrag van bijvoorbeeld de First methode, waaraan kan worden voldaan nadat de eerste match is gevonden.

Behalve

De methode Except retourneert de set items die zich in de eerste verzameling bevinden, maar niet in de tweede. De standaard IEqualityComparer wordt gebruikt om de items binnen de twee sets te vergelijken. Er is een overbelasting die een IEqualityComparer als argument accepteert.

Voorbeeld:

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

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

Output:

1
4

Live demo op .NET Fiddle

In dit geval .Except(second) elementen uit de second array uit, namelijk 2 en 3 (0 en 5 zijn niet opgenomen in de first array en worden overgeslagen).

Merk op dat Except impliciet Distinct betekent (dwz het verwijdert herhaalde elementen). Bijvoorbeeld:

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

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

Output:

1
4

Live demo op .NET Fiddle

In dit geval worden de elementen 1 en 4 slechts één keer geretourneerd.


Door IEquatable implementeren of de functie een IEqualityComparer , kan een andere methode worden gebruikt om de elementen te vergelijken. Merk op dat de GetHashCode methode ook moet worden genegeerd, zodat deze een identieke hash-code retourneert voor object die identiek zijn volgens de IEquatable implementatie.

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

Output:

Chanoeka

Live demo op .NET Fiddle

SelectMany: Een reeks reeksen afvlakken

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 }

Gebruik SelectMany() als u dat hebt, of u maakt een reeks reeksen, maar u wilt het resultaat als één lange reeks.

In LINQ Query Syntax:

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

Als u een verzameling collecties heeft en tegelijkertijd aan gegevens uit de collectie van ouders en kinderen wilt kunnen werken, is dit ook mogelijk met SelectMany .

Laten we eenvoudige klassen definiëren

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

Laten we aannemen dat we de volgende verzameling hebben.

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

Nu willen we reacties Content selecteren samen met Id van BlogPost die aan deze reactie is gekoppeld. Om dit te doen, kunnen we de juiste SelectMany overbelasting gebruiken.

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

Onze commentsWithIds Id's zien er zo uit

{
    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

De SelectMany linq-methode 'vlakt' een IEnumerable<IEnumerable<T>> in een IEnumerable<T> . Alle T-elementen binnen de IEnumerable instanties in de bron IEnumerable zullen worden gecombineerd tot een enkele IEnumerable .

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

Als u een selectorfunctie gebruikt die invoerelementen omzet in reeksen, worden de elementen van die reeksen één voor één geretourneerd.

Merk op dat, in tegenstelling tot Select() , het aantal elementen in de uitvoer niet hetzelfde hoeft te zijn als in de invoer.

Meer real-world voorbeeld

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

Output:

Bob
vijzel
Jim
John

Live demo op .NET Fiddle

Allemaal

All wordt gebruikt om te controleren of alle elementen van een verzameling overeenkomen met een voorwaarde of niet.
zie ook: Je verder

1. Lege parameter

Alles : mag niet worden gebruikt met lege parameter.

2. Lambda-expressie als parameter

All : Retourneert true als alle elementen van de verzameling voldoen aan de lambda-expressie en anders 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. Collectie leegmaken

All : Retourneert true als de verzameling leeg is en een lambda-expressie is opgegeven:

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

Opmerking: All stoppen de iteratie van de verzameling zodra een element wordt gevonden dat niet overeenkomt met de voorwaarde. Dit betekent dat de verzameling niet noodzakelijk volledig zal worden opgesomd; het wordt alleen ver genoeg opgesomd om het eerste item te vinden dat niet overeenkomt met de voorwaarde.

Zoekopdrachtverzameling op type / cast-elementen om te typen

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 

Unie

Voegt twee collecties samen om een aparte collectie te maken met behulp van de standaardvergelijkingsvergelijker

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 op .NET Fiddle

DOET MEE

Joins worden gebruikt om verschillende lijsten of tabellen met gegevens via een gemeenschappelijke sleutel te combineren.

Net als in SQL worden de volgende soorten Joins ondersteund in LINQ:
Binnen, links, rechts, kruis en volledige buitenste verbindingen.

De volgende twee lijsten worden in de onderstaande voorbeelden gebruikt:

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

(Inner) Doe mee

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

Linker buitenvoeg

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

Volledige deelname aan de buitenwereld

var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);

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

Praktisch voorbeeld

De bovenstaande voorbeelden hebben een eenvoudige gegevensstructuur, zodat u zich kunt concentreren op het technisch begrijpen van de verschillende LINQ-joins, maar in de echte wereld zou u tabellen hebben met kolommen waaraan u moet deelnemen.

In het volgende voorbeeld wordt er slechts één klasse Region gebruikt, in werkelijkheid zou u twee of meer verschillende tabellen samenvoegen die dezelfde sleutel bevatten (in dit voorbeeld worden first en second verbonden via de gemeenschappelijke sleutel- ID ).

Voorbeeld: overweeg de volgende gegevensstructuur:

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

Bereid nu de gegevens voor (dwz vul met gegevens):

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

Je kunt zien dat in dit voorbeeld first geen regiobeschrijvingen bevat, dus je wilt vanaf het second . Dan ziet de binnenste verbinding eruit:

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

Dit resultaat heeft ter plekke anonieme objecten gemaakt, wat prima is, maar we hebben al een juiste klasse gemaakt - dus we kunnen deze opgeven: in plaats van select new { f.ID, s.RegionDescription }; we kunnen zeggen select new Region(f.ID, s.RegionDescription); , die dezelfde gegevens retourneert, maar objecten van het type Region - die compatibel blijven met de andere objecten.

Live demo op .NET viool

onderscheiden

Retourneert unieke waarden uit een IEnumerable . Uniciteit wordt bepaald met behulp van de standaardvergelijkingsvergelijker.

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

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

Om een aangepast gegevenstype te vergelijken, moeten we de IEquatable<T> -interface implementeren en GetHashCode en Equals methoden voor het type bieden. Of de gelijkheidsvergelijker kan worden opgeheven:

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

Groep Door één of meerdere velden

Laten we aannemen dat we een filmmodel hebben:

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

Groeperen op categorie:

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

Groeperen op categorie en jaar:

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

Bereik gebruiken met verschillende Linq-methoden

U kunt de klasse Enumerable naast Linq-query's gebruiken om lussen om te zetten in Linq one-liners.

Selecteer Voorbeeld

In tegenstelling tot dit:

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

Je kan dit doen:

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

Waar bijvoorbeeld

In dit voorbeeld worden 100 nummers gegenereerd en even nummers worden geëxtraheerd

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

Zoekopdracht bestellen - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()

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

oplopend:

Zoekopdrachtsyntaxis

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

Methode Syntaxis

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

sortNames bevat de namen in de volgende volgorde: "adam", "mark", "steve"

Aflopend:

Zoekopdrachtsyntaxis

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

Methode Syntaxis

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

sortNames bevat de namen in de volgende volgorde: "steve", "mark", "adam"

Sorteer op verschillende velden

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

Zoekopdrachtsyntaxis

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

Methode Syntaxis

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

Resultaat

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

Basics

LINQ is grotendeels nuttig voor het doorzoeken van collecties (of arrays).

Bijvoorbeeld, gegeven de volgende voorbeeldgegevens:

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

We kunnen deze gegevens "opvragen" met de LINQ-syntaxis. Om bijvoorbeeld alle studenten op te halen die vandaag een snack hebben:

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

Of om studenten met een cijfer van 90 of hoger op te halen en alleen hun namen te retourneren, niet het volledige Student object:

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

De LINQ-functie bestaat uit twee syntaxis die dezelfde functies uitvoeren, bijna identieke prestaties hebben, maar heel anders zijn geschreven. De syntaxis in het bovenstaande voorbeeld wordt querysyntaxis genoemd . Het volgende voorbeeld illustreert de syntaxis van de methode . Dezelfde gegevens worden geretourneerd als in het bovenstaande voorbeeld, maar de manier waarop de query is geschreven, is anders.

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

GroupBy

GroupBy is een gemakkelijke manier om een IEnumerable<T> verzameling items in verschillende groepen te sorteren.

Eenvoudig voorbeeld

In dit eerste voorbeeld eindigen we met twee groepen, oneven en even items.

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

Complexer voorbeeld

Laten we een groep mensen op leeftijd als voorbeeld nemen. Eerst maken we een Person-object met twee eigenschappen, Naam en Leeftijd.

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

Vervolgens maken we onze voorbeeldlijst van mensen met verschillende namen en leeftijden.

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

Vervolgens maken we een LINQ-query om onze lijst met mensen te groeperen op leeftijd.

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

Als we dit doen, kunnen we de leeftijd voor elke groep bekijken en een lijst van elke persoon in de groep hebben.

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

Dit resulteert in de volgende uitvoer:

20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith

Je kunt spelen met de live demo op .NET Fiddle

Ieder

Any wordt gebruikt om te controleren of een element van een verzameling aan een voorwaarde voldoet of niet.
zie ook: .All , Any en FirstOrDefault: best practice

1. Lege parameter

Willekeurig : geeft true als de verzameling elementen bevat en false als de verzameling leeg is:

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-expressie als parameter

Willekeurig : geeft true als de verzameling een of meer elementen heeft die voldoen aan de voorwaarde in de lambda-expressie:

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

3. Collectie leegmaken

Any : Retourneert false als de verzameling leeg is en een lambda-expressie is opgegeven:

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

Opmerking: Any stopt de iteratie van de verzameling zodra deze een element vindt dat overeenkomt met de voorwaarde. Dit betekent dat de verzameling niet noodzakelijk volledig zal worden opgesomd; het wordt alleen ver genoeg opgesomd om het eerste item te vinden dat overeenkomt met de voorwaarde.

Live demo op .NET Fiddle

ToDictionary

De methode ToDictionary() LINQ kan worden gebruikt om een Dictionary<TKey, TElement> te genereren op basis van een gegeven IEnumerable<T> Dictionary<TKey, TElement> .

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

In dit voorbeeld is het enige argument dat is doorgegeven aan ToDictionary van het type Func<TSource, TKey> , dat de sleutel voor elk element retourneert.

Dit is een beknopte manier om de volgende bewerking uit te voeren:

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

U kunt ook een tweede parameter doorgeven aan de methode ToDictionary , die van het type Func<TSource, TElement> en de Value retourneert die moet worden toegevoegd voor elk item.

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

Het is ook mogelijk om de IComparer te geven die wordt gebruikt om sleutelwaarden te vergelijken. Dit kan handig zijn als de sleutel een string is en u wilt dat deze niet hoofdlettergevoelig is.

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

Opmerking: de ToDictionary methode vereist dat alle sleutels uniek zijn, er mogen geen dubbele sleutels zijn. Als dit het geval is, wordt een uitzondering gegenereerd: ArgumentException: An item with the same key has already been added. Als u een scenario hebt waarvan u weet dat u meerdere elementen met dezelfde sleutel hebt, kunt u beter in plaats daarvan ToLookup gebruiken.

Aggregaat

Aggregate Past een accumulatiefunctie toe op een reeks.

int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
  • Bij de eerste stap prevSum = 1
  • Bij de tweede prevSum = prevSum(at the first step) + 2
  • Bij de i-de stap 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 !"

Een tweede overbelasting van Aggregate ontvangt ook een seed parameter die de initiële accumulatorwaarde is. Dit kan worden gebruikt om meerdere voorwaarden voor een verzameling te berekenen zonder deze meerdere keren te herhalen.

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

Voor de verzameling items we willen berekenen

  1. Het totale .Count
  2. Het aantal even getallen
  3. Verzamel elk vierde item

Met Aggregate kan het als volgt worden gedaan:

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]

Merk op dat bij het gebruik van een anoniem type als seed elk item een nieuw object moet maken, omdat de eigenschappen alleen-lezen zijn. Met behulp van een aangepaste klasse kan men eenvoudig de informatie toewijzen en is er geen new nodig (alleen bij het geven van de initiële seed parameter

Een variabele in een Linq-query definiëren (laat trefwoord)

Om een variabele in een linq-uitdrukking te definiëren, kunt u het sleutelwoord let gebruiken . Dit wordt meestal gedaan om de resultaten van tussentijdse subquery's op te slaan, bijvoorbeeld:

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

Output:

Het gemiddelde van de getallen is 4,5.
Zoekopdrachtresultaat omvat nummer 3 met vierkant van 9.
Zoekopdrachtresultaat omvat nummer 4 met vierkant van 16.
Zoekopdrachtresultaat omvat nummer 5 met vierkant van 25.
Zoekopdrachtresultaat bevat nummer 6 met vierkant van 36.
Zoekopdrachtresultaat omvat nummer 7 met vierkant van 49.
Zoekopdrachtresultaat omvat nummer 8 met vierkant van 64.
Zoekopdrachtresultaat omvat nummer 9 met vierkant van 81.

Demo bekijken

SkipWhile

SkipWhile() wordt gebruikt om elementen uit te sluiten tot de eerste niet-overeenkomst (dit kan voor de meeste mensen intuïtief zijn)

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

DefaultIfEmpty

DefaultIfEmpty wordt gebruikt om een standaardelement te retourneren als de reeks geen elementen bevat. Dit element kan de standaardwaarde van het type zijn of een door de gebruiker gedefinieerd exemplaar van dat type. Voorbeeld:

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;

Gebruik in Left Joins :

Met DefaultIfEmpty de traditionele Linq Join een standaardobject retourneren als er geen overeenkomst is gevonden. Dus fungeert als een SQL's Left Join. Voorbeeld:

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

In het geval dat een DefaultIfEmpty wordt gebruikt (zonder een standaardwaarde op te geven) en dat zal resulteren in geen overeenkomende items in de juiste volgorde, moet u ervoor zorgen dat het object niet null voordat u toegang krijgt tot de eigenschappen. Anders zal het resulteren in een NullReferenceException . Voorbeeld:

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 wordt gebruikt om twee IEnumerable<T> -reeksen met elkaar te vergelijken.

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

Count retourneert het aantal elementen in een IEnumerable<T> . Count toont ook een optionele predicaatparameter waarmee u de elementen kunt filteren die u wilt tellen.

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 werkt op dezelfde manier als Count maar heeft een return-type long en wordt gebruikt voor het tellen van int.MaxValue IEnumerable<T> int.MaxValue die langer zijn dan 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

Stapsgewijs een zoekopdracht maken

Omdat LINQ uitgestelde uitvoering gebruikt , kunnen we een queryobject hebben dat de waarden niet bevat, maar de waarden retourneert wanneer deze worden geëvalueerd. We kunnen de query dus dynamisch bouwen op basis van onze controlestroom en deze evalueren zodra we klaar zijn:

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

We kunnen voorwaardelijk filters toepassen:

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

We kunnen een sorteervolgorde toevoegen aan de query op basis van een voorwaarde:

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

Onze zoekopdracht kan worden gedefinieerd om te beginnen vanaf een bepaald punt:

    query = query.Skip(start - 1);

en gedefinieerd om een specifiek aantal records te retourneren:

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

Zodra we het queryobject hebben, kunnen we de resultaten evalueren met een foreach lus of een van de LINQ-methoden die een reeks waarden retourneert, zoals ToList of ToArray :

SearchModel sm;

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

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

ritssluiting

De Zip uitbreidingsmethode werkt op twee collecties. Het koppelt elk element in de twee reeksen samen op basis van positie. Met een Func instantie gebruiken we Zip om elementen uit de twee C # -collecties paarsgewijs te verwerken. Als de reeks in grootte verschilt, worden de extra elementen van de grotere reeks genegeerd.

Om een voorbeeld te nemen uit het boek "C # in een notendop",

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

Output:

3 = drie
5 = vijf
7 = zeven

Demo bekijken

GroupJoin met variabele buitenbereik

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

ElementAt retourneert het item op index n . Als n niet binnen het bereik van de opsomming valt, werpt een ArgumentOutOfRangeException .

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

ElementAtOrDefault retourneert het item op index n . Als n niet binnen het bereik van de opsomming valt, wordt een default(T) geretourneerd.

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

Zowel ElementAt als ElementAtOrDefault zijn geoptimaliseerd voor wanneer de bron een IList<T> en in die gevallen wordt normale indexering gebruikt.

Merk op dat voor ElementAt , als de verstrekte index groter is dan de grootte van de IList<T> , de lijst (maar technisch niet gegarandeerd) een ArgumentOutOfRangeException moet IList<T> .

Linq-kwantificatoren

Kwantificeerbewerkingen retourneren een Booleaanse waarde als sommige of alle elementen in een reeks aan een voorwaarde voldoen. In dit artikel zullen we enkele veel voorkomende LINQ naar objecten-scenario's zien waar we deze operators kunnen gebruiken. Er zijn 3 Quantifiers-bewerkingen die in LINQ kunnen worden gebruikt:

All - wordt gebruikt om te bepalen of alle elementen in een reeks aan een voorwaarde voldoen. bv:

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 - wordt gebruikt om te bepalen of elementen in een reeks aan een voorwaarde voldoen. bv:

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

Contains - wordt gebruikt om te bepalen of een reeks een opgegeven element bevat. bv:

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

Samenvoegen van meerdere reeksen

Beschouw entiteiten Customer , Purchase en PurchaseItem als volgt:

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

Overweeg de volgende voorbeeldgegevens voor bovenstaande entiteiten:

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

Overweeg nu onderstaande linq-query:

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

Om het resultaat van bovenstaande zoekopdracht uit te voeren:

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

De uitvoer van de zoekopdracht zou zijn:

Klant1, Klant1-Aankoop1, Aankoop1-Aankoopartikel1

Klant1, Klant1-Aankoop2, Aankoop2-Aankoopartikel1

Klant1, Klant1-Aankoop2, Aankoop2-Aankoopartikel2

Klant2, Klant2-Aankoop2, Aankoop3-Aankoopartikel1

Live demo op .NET Fiddle

Deelnemen aan meerdere toetsen

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

Houd er rekening mee dat anonieme typen in bovenstaande join dezelfde eigenschappen moeten bevatten, omdat objecten alleen als gelijk worden beschouwd als al hun eigenschappen gelijk zijn. Anders wordt de zoekopdracht niet gecompileerd.

Selecteer met Func selector - Gebruik deze om een rangorde van elementen te krijgen

Een van de overbelastingen van de Select uitbreidingsmethoden geeft ook de index van het huidige item in de collectie die wordt select . Dit zijn enkele toepassingen.

Krijg het "rijnummer" van de items

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

Krijg de rang van een item binnen zijn groep

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

Krijg de rangorde van groepen (ook bekend in Oracle als 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();

Om dit te testen kunt u gebruiken:

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

En gegevens:

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 retourneert elementen uit een reeks zolang de voorwaarde waar is

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

Som

De uitbreidingsmethode Enumerable.Sum berekent de som van numerieke waarden.

Als de elementen van de collectie zelf getallen zijn, kunt u de som direct berekenen.

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

Als het type elementen een complex type is, kunt u een lambda-expressie gebruiken om de waarde op te geven die moet worden berekend:

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

De somuitbreidingsmethode kan met de volgende typen berekenen:

  • Int32
  • Int64
  • single
  • Dubbele
  • Decimale

In het geval dat uw verzameling nulbare typen bevat, kunt u de operator null-coalescing gebruiken om een standaardwaarde voor null-elementen in te stellen:

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

Opkijken

ToLookup retourneert een gegevensstructuur die indexering mogelijk maakt. Het is een uitbreidingsmethode. Het produceert een ILookup-instantie die kan worden geïndexeerd of geteld met behulp van een foreach-lus. De vermeldingen worden bij elke toets samengevoegd in groepen. - 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

Een ander voorbeeld:

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

Bouw je eigen Linq-operators voor IEnumerable

Een van de geweldige dingen van Linq is dat het zo gemakkelijk is uit te breiden. U hoeft alleen een extensiemethode te maken waarvan het 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 dit voorbeeld worden de items in een IEnumerable<T> in lijsten met een vaste grootte, waarbij de laatste lijst de rest van de items bevat. Merk op hoe het object waarop het toestel wordt toegepast wordt doorgegeven (argument source ) als beginargument met het this zoekwoord. Vervolgens wordt het yield trefwoord gebruikt om het volgende item in de output IEnumerable<T> alvorens vanaf dat punt verder te gaan met uitvoeren (zie yield-trefwoord ).

Dit voorbeeld zou als volgt in uw code worden gebruikt:

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

In de eerste lus zou de sublijst {2, 3, 4} en in de tweede {5, 6} .

Aangepaste LinQ-methoden kunnen ook worden gecombineerd met standaard LinQ-methoden. bijvoorbeeld:

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

Deze zoekopdracht retourneert even nummers gegroepeerd in batches met een grootte van 3: {0, 2, 4}, {6, 8, 10}, {12}

Vergeet niet dat using MyNamespace; nodig using MyNamespace; regel om toegang te krijgen tot de uitbreidingsmethode.

SelectMany gebruiken in plaats van geneste lussen

Gegeven 2 lijsten

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

als u alle permutaties wilt uitvoeren, kunt u geneste lussen gebruiken, zoals

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

Met SelectMany kunt u dezelfde bewerking uitvoeren als

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

Any and First (OrDefault) - beste praktijk

Ik zal niet uitleggen wat Any en FirstOrDefault doet omdat er al twee goede voorbeelden over zijn. Zie Any en First, FirstOrDefault, Last, LastOrDefault, Single en SingleOrDefault voor meer informatie.

Een patroon dat ik vaak in code zie en dat moet worden vermeden, is

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

Het zou zo efficiënter kunnen worden geschreven

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

Aan de hand van het tweede voorbeeld wordt de verzameling slechts één keer doorzocht en geeft hetzelfde resultaat als de eerste. Hetzelfde idee kan worden toegepast op Single .

GroupBy Sum and Count

Laten we een voorbeeldklasse nemen:

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

Laten we nu een lijst met transacties bekijken:

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

Als u de categoriegewijze som van het bedrag en het aantal wilt berekenen, kunt u GroupBy als volgt gebruiken:

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

Als alternatief kunt u dit in één stap doen:

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

De uitvoer voor beide bovenstaande zoekopdrachten zou hetzelfde zijn:

Categorie: Saving Account, Bedrag: 66, Aantal: 2

Categorie: Creditcard, Bedrag: 71, Aantal: 2

Categorie: Zichtrekening, Bedrag: 100, Aantal: 1

Live demo in .NET Fiddle

Omgekeerde

  • Keert de volgorde van de elementen in een reeks om.
  • Als er geen items zijn, wordt een ArgumentNullException: source is null.

Voorbeeld:

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

Onthoud dat Reverse() kan werken, afhankelijk van de volgorde van uw LINQ-instructies.

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

Reverse () werkt door alles te bufferen en er dan achteruit doorheen te lopen, wat niet erg efficiënt is, maar OrderBy ook niet vanuit dat perspectief.

In LINQ naar objecten zijn er bufferbewerkingen (Reverse, OrderBy, GroupBy, enz.) En niet-bufferende bewerkingen (Where, Take, Skip, enz.).

Voorbeeld: niet-buffering Extensie verlengen

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 voorbeeld

Deze methode kan problemen tegenkomen als u de lijst muteert tijdens het itereren.

De opsomming opsommen

De IEnumerable <T> -interface is de basisinterface voor alle generieke enumerators en is een essentieel onderdeel van het begrijpen van LINQ. In de kern vertegenwoordigt het de volgorde.

Deze onderliggende interface wordt overgenomen door alle generieke collecties, zoals Collection <T> , Array , List <T> , Dictionary <TKey, TValue> Class en HashSet <T> .

Naast het vertegenwoordigen van de reeks, moet elke klasse die erft van IEnumerable <T> een IEnumerator <T> leveren. De teller noemt de iterator voor het opsombare, en deze twee onderling verbonden interfaces en ideeën vormen de bron van het gezegde "het opsombare opsommen".

"Het opsommen van de opsomming" is een belangrijke zin. Het opsombare is eenvoudig een structuur voor het itereren, het bevat geen gematerialiseerde objecten. Bij het sorteren kan bijvoorbeeld een opsomming de criteria bevatten van het veld om te sorteren, maar het gebruik van .OrderBy() op zichzelf .OrderBy() een IEnumerable <T> op die alleen weet hoe te sorteren. Het gebruik van een aanroep die de objecten materialiseert, zoals bij het herhalen van de set, staat bekend als opsommen (bijvoorbeeld .ToList() ). Het opsommingsproces gebruikt de opsomming van hoe om door de reeks te bladeren en de relevante objecten te retourneren (in volgorde, gefilterd, geprojecteerd, enz.).

Pas als de opsomming is opgesomd, veroorzaakt dit de materialisatie van de objecten, dat is wanneer metrieken als tijdcomplexiteit (hoe lang dit moet duren in verband met de reeksgrootte) en ruimtelijke complexiteit (hoeveel ruimte het moet gebruiken in verband met de reeksgrootte) worden gemeten.

Het maken van uw eigen klasse die erft van IEnumerable <T> kan een beetje ingewikkeld zijn, afhankelijk van de onderliggende reeks die moet worden opgesomd. Over het algemeen is het het beste om een van de bestaande generieke collecties te gebruiken. Dat gezegd hebbende, is het ook mogelijk om van de IEnumerable <T> interface te erven zonder een gedefinieerde array als onderliggende structuur te hebben.

Gebruik bijvoorbeeld de Fibonacci-serie als onderliggende reeks. Merk op dat de aanroep van Where eenvoudigweg een IEnumerable bouwt, en het is pas nadat een aanroeping tot opsomming is gedaan dat elk van de waarden wordt gematerialiseerd.

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

uitgang

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

De kracht in de tweede set (de fibMod612) is dat hoewel we de oproep hebben gedaan om onze hele set Fibonacci-nummers te bestellen, omdat slechts één waarde is gebruikt met. .First() de tijdcomplexiteit O (n) als slechts 1 waarde moest worden vergeleken tijdens de uitvoering van het bestelalgoritme. Dit komt omdat onze teller slechts om 1 waarde vroeg en dus niet de hele opsomming behoefde te worden uitgevoerd. Als we .Take(5) hadden gebruikt in plaats van. .First() , zou de teller 5 waarden hebben gevraagd en zouden er maximaal 5 waarden moeten worden gematerialiseerd. Vergeleken met de noodzaak om een hele set te bestellen en vervolgens de eerste 5 waarden te nemen, bespaart het principe van veel uitvoeringstijd en -ruimte.

OrderBy

Hiermee bestelt u een collectie met een opgegeven waarde.

Wanneer de waarde een geheel getal , dubbel of zwevend is, begint deze met de minimumwaarde , wat betekent dat u eerst de negatieve waarden krijgt, dan nul en daarna de positieve waarden (zie Voorbeeld 1).

Wanneer u bestelt met een teken , vergelijkt de methode de ascii-waarden van de tekens om de verzameling te sorteren (zie Voorbeeld 2).

Wanneer u tekenreeksen sorteert, vergelijkt de OrderBy-methode ze door hun CultureInfo te bekijken, maar normaal beginnende met de eerste letter in het alfabet (a, b, c ...).

Dit soort volgorde wordt oplopend genoemd, als je het andersom wilt, moet je afdalen (zie OrderByDescending).

Voorbeeld 1:

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

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

Voorbeeld:

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

Hiermee bestelt u een collectie met een opgegeven waarde.

Wanneer de waarde een geheel getal , dubbel of zwevend is, begint deze met de maximale waarde , wat betekent dat u eerst de positieve waarden krijgt, dan nul en daarna de negatieve waarden (zie Voorbeeld 1).

Wanneer u bestelt met een teken , vergelijkt de methode de ascii-waarden van de tekens om de verzameling te sorteren (zie Voorbeeld 2).

Wanneer u tekenreeksen sorteert, vergelijkt de OrderBy-methode ze door hun CultureInfo te bekijken, maar normaal beginnende met de laatste letter in het alfabet (z, y, x, ...).

Dit soort volgorde wordt aflopend genoemd, als je het andersom wilt, moet je oplopend zijn (zie OrderBy).

Voorbeeld 1:

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

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

Voorbeeld 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

Voegt twee collecties samen (zonder duplicaten te verwijderen)

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

bevat

MSDN:

Bepaalt of een reeks een opgegeven element bevat met behulp van een opgegeven 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

Een door de gebruiker gedefinieerd object gebruiken:

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

Gebruik van de Enumerable.Contains(value, comparer) overbelasting:

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

Een slim gebruik van Contains zou zijn om meerdere if clausules te vervangen voor een Contains aanroep.

Dus in plaats van dit te doen:

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

Doe dit:

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow