Recherche…


Introduction

LINQ est un acronyme qui signifie L anguage IN ITéS Q uery. C'est un concept qui intègre un langage de requête en offrant un modèle cohérent pour travailler avec des données sur différents types de sources de données et de formats. Vous utilisez les mêmes modèles de codage de base pour interroger et transformer des données dans des documents XML, des bases de données SQL, des ensembles de données ADO.NET, des collections .NET et tout autre format pour lequel un fournisseur LINQ est disponible.

Syntaxe

  • Syntaxe de requête:

    • de <variable de plage> dans <collection>
    • [de <variable de plage> dans <collection>, ...]
    • <filtrer, joindre, grouper, agréger des opérateurs, ...> <expression lambda>
    • <opérateur select ou groupBy> <formuler le résultat>
  • Syntaxe de la méthode:

    • Enumerable.Aggregate (func)
    • Enumerable.Aggregate (graine, func)
    • Enumerable.Aggregate (graine, func, resultSelector)
    • Enumerable.All (prédicat)
    • Enumerable.Any ()
    • Enumerable.Any (prédicat)
    • Enumerable.AsEnumerable ()
    • Enumerable.Average ()
    • Enumerable.Average (sélecteur)
    • Enumerable.Cast <Résultat> ()
    • Enumerable.Concat (second)
    • Enumerable.Contains (value)
    • Enumerable.Contains (valeur, comparateur)
    • Enumerable.Count ()
    • Enumerable.Count (prédicat)
    • Enumerable.DefaultIfEmpty ()
    • Enumerable.DefaultIfEmpty (defaultValue)
    • Enumerable.Distinct ()
    • Enumerable.Distinct (comparateur)
    • Enumerable.ElementAt (index)
    • Enumerable.ElementAtOrDefault (index)
    • Enumerable.Empty ()
    • Enumerable.Except (second)
    • Enumerable.Except (second, comparateur)
    • Enumerable.First ()
    • Enumerable.First (prédicat)
    • Enumerable.FirstOrDefault ()
    • Enumerable.FirstOrDefault (prédicat)
    • Enumerable.GroupBy (keySelector)
    • Enumerable.GroupBy (keySelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector)
    • Enumerable.GroupBy (keySelector, comparateur)
    • Enumerable.GroupBy (keySelector, resultSelector, comparateur)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector, comparateur)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector, comparateur)
    • Enumerable.Intersect (second)
    • Enumerable.Intersect (second, comparateur)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, comparateur)
    • Enumerable.Last ()
    • Enumerable.Last (prédicat)
    • Enumerable.LastOrDefault ()
    • Enumerable.LastOrDefault (prédicat)
    • Enumerable.LongCount ()
    • Enumerable.LongCount (prédicat)
    • Enumerable.Max ()
    • Enumerable.Max (sélecteur)
    • Enumerable.Min ()
    • Enumerable.Min (sélecteur)
    • Enumerable.OfType <TResult> ()
    • Enumerable.OrderBy (keySelector)
    • Enumerable.OrderBy (keySelector, comparateur)
    • Enumerable.OrderByDescending (keySelector)
    • Enumerable.OrderByDescending (keySelector, comparateur)
    • Enumerable.Range (démarrer, compter)
    • Enumerable.Repeat (element, count)
    • Enumerable.Reverse ()
    • Enumerable.Select (sélecteur)
    • Enumerable.SelectMany (sélecteur)
    • Enumerable.SelectMany (collectionSelector, resultSelector)
    • Enumerable.SequenceEqual (second)
    • Enumerable.SequenceEqual (second, comparateur)
    • Enumerable.Single ()
    • Enumerable.Single (prédicat)
    • Enumerable.SingleOrDefault ()
    • Enumerable.SingleOrDefault (prédicat)
    • Enumerable.Skip (count)
    • Enumerable.SkipWhile (prédicat)
    • Enumerable.Sum ()
    • Enumerable.Sum (sélecteur)
    • Enumerable.Take (count)
    • Enumerable.TakeWhile (prédicat)
    • orderEnumerable.ThenBy (keySelector)
    • orderEnumerable.ThenBy (keySelector, comparateur)
    • orderEnumerable.ThenByDescending (keySelector)
    • OrdreEnumerable.ThenByDescending (keySelector, comparateur)
    • Enumerable.ToArray ()
    • Enumerable.ToDictionary (keySelector)
    • Enumerable.ToDictionary (keySelector, elementSelector)
    • Enumerable.ToDictionary (keySelector, comparateur)
    • Enumerable.ToDictionary (keySelector, elementSelector, comparateur)
    • Enumerable.ToList ()
    • Enumerable.ToLookup (keySelector)
    • Enumerable.ToLookup (keySelector, elementSelector)
    • Enumerable.ToLookup (keySelector, comparateur)
    • Enumerable.ToLookup (keySelector, elementSelector, comparateur)
    • Enumerable.Union (second)
    • Enumerable.Union (second, comparateur)
    • Enumerable.Where (prédicat)
    • Enumerable.Zip (second, resultSelector)

Remarques

Pour utiliser les requêtes LINQ, vous devez importer System.Linq .

La syntaxe de la méthode est plus puissante et flexible, mais la syntaxe de la requête peut être plus simple et plus familière. Toutes les requêtes écrites dans la syntaxe Query sont traduites dans la syntaxe fonctionnelle par le compilateur. Les performances sont donc les mêmes.

Les objets de requête ne sont pas évalués tant qu'ils ne sont pas utilisés, ils peuvent donc être modifiés ou ajoutés sans pénalité de performance.

Retourne un sous-ensemble d'éléments dont le prédicat spécifié est vrai pour eux.

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

Syntaxe de la méthode

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

Syntaxe de requête

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

Sélectionner - Éléments de transformation

Select vous permet d'appliquer une transformation à chaque élément de toute structure de données implémentant IEnumerable.

Obtenir le premier caractère de chaque chaîne dans la liste suivante:

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

Utiliser la syntaxe régulière (lambda)

//The below select stament transforms each element in tree into its first character.
IEnumerable<String> initials = trees.Select(tree => tree.Substring(0, 1));
foreach (String initial in initials) {
    System.Console.WriteLine(initial);
}

Sortie:

O
B
B
E
H
M

Démo en direct sur .NET Fiddle

Utiliser la syntaxe de requête LINQ

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

Méthodes de chaînage

De nombreuses fonctions LINQ opèrent toutes deux sur un IEnumerable<TSource> et renvoient également un IEnumerable<TResult> . Les paramètres de type TSource et TResult peuvent ou non se référer au même type, selon la méthode en question et les fonctions qui lui sont transmises.

Quelques exemples de ceci sont

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
)

Alors que certains chaînages de méthodes peuvent nécessiter l'utilisation d'un ensemble complet avant de passer à un autre, LINQ tire parti de l' exécution différée en utilisant MSDN de rendement qui crée un Enumerable et un Enumerator en arrière-plan. Le processus de chaînage dans LINQ consiste essentiellement à créer un enumerable (itérateur) pour l’ensemble original - qui est différé - jusqu’à ce qu’il soit matérialisé en énumérant l’énumérable .

Cela permet à ces fonctions d'être couramment enchaînées sur wiki , où une fonction peut agir directement sur le résultat d'une autre. Ce style de code peut être utilisé pour effectuer de nombreuses opérations basées sur des séquences dans une seule instruction.

Par exemple, il est possible de combiner Select , Where et OrderBy pour transformer, filtrer et trier une séquence en une seule instruction.

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

Sortie:

2
4
8

Démo en direct sur .NET Fiddle

Toutes les fonctions qui étendent et retournent le type générique IEnumerable<T> peuvent être utilisées comme clauses chaînées dans une seule instruction. Ce style de programmation fluide est puissant et doit être pris en compte lors de la création de vos propres méthodes d'extension .

Portée et répétition

Les méthodes statiques Range et Repeat sur Enumerable peuvent être utilisées pour générer des séquences simples.

Gamme

Enumerable.Range() génère une séquence d'entiers à partir d'une valeur de départ et d'un nombre.

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

Démo en direct sur .NET Fiddle

Répéter

Enumerable.Repeat() génère une séquence d'éléments répétés à partir d'un élément et du nombre de répétitions requis.

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

Démo en direct sur .NET Fiddle

Passer et prendre

La méthode Skip renvoie une collection à l'exclusion d'un certain nombre d'éléments du début de la collection source. Le nombre d'éléments exclus est le nombre donné en argument. Si la collection contient moins d'éléments que ceux spécifiés dans l'argument, une collection vide est renvoyée.

La méthode Take renvoie une collection contenant un certain nombre d'éléments du début de la collection source. Le nombre d'éléments inclus est le nombre donné en argument. Si la collection contient moins d'éléments que spécifié dans l'argument, la collection renvoyée contiendra les mêmes éléments que la collection source.

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

Démo en direct sur .NET Fiddle

Skip and Take sont couramment utilisés ensemble pour paginer les résultats, par exemple:

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

Avertissement: LINQ to Entities ne prend en charge que les requêtes de saut sur les commandes . Si vous essayez d'utiliser Skip sans passer commande, vous recevrez une exception NotSupportedException avec le message "La méthode 'Skip' est uniquement prise en charge pour les entrées triées dans LINQ to Entities. La méthode 'OrderBy' doit être appelée avant la méthode 'Skip'."

Tout d'abord, FirstOrDefault, Last, LastOrDefault, Single et SingleOrDefault

Les six méthodes renvoient une seule valeur du type de séquence et peuvent être appelées avec ou sans prédicat.

Selon le nombre d'éléments correspondant au predicate ou, si aucun predicate n'est fourni, le nombre d'éléments de la séquence source, ils se comportent comme suit:

Premier()

  • Renvoie le premier élément d'une séquence ou le premier élément correspondant au predicate fourni.
  • Si la séquence ne contient aucun élément, une InvalidOperationException est lancée avec le message: "La séquence ne contient aucun élément".
  • Si la séquence ne contient aucun élément correspondant au predicate fourni, une InvalidOperationException est InvalidOperationException avec le message "La séquence ne contient aucun élément correspondant".

Exemple

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

Démo en direct sur .NET Fiddle

FirstOrDefault ()

  • Renvoie le premier élément d'une séquence ou le premier élément correspondant au predicate fourni.
  • Si la séquence ne contient aucun élément ou aucun élément correspondant au predicate fourni, retourne la valeur par défaut du type de séquence en utilisant la default(T) par default(T) .

Exemple

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

Démo en direct sur .NET Fiddle

Dernier()

  • Renvoie le dernier élément d'une séquence ou le dernier élément correspondant au predicate fourni.
  • Si la séquence ne contient aucun élément, une InvalidOperationException est lancée avec le message "La séquence ne contient aucun élément".
  • Si la séquence ne contient aucun élément correspondant au predicate fourni, une InvalidOperationException est InvalidOperationException avec le message "La séquence ne contient aucun élément correspondant".

Exemple

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

  • Renvoie le dernier élément d'une séquence ou le dernier élément correspondant au predicate fourni.
  • Si la séquence ne contient aucun élément ou aucun élément correspondant au predicate fourni, retourne la valeur par défaut du type de séquence en utilisant la default(T) par default(T) .

Exemple

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

Unique()

  • Si la séquence contient exactement un élément ou exactement un élément correspondant au predicate fourni, cet élément est renvoyé.
  • Si la séquence ne contient aucun élément ou aucun élément correspondant au predicate fourni, une InvalidOperationException est lancée avec le message "La séquence ne contient aucun élément".
  • Si la séquence contient plusieurs éléments ou plusieurs éléments correspondant au predicate fourni, une InvalidOperationException est lancée avec le message "La séquence contient plusieurs éléments".
  • Remarque: pour évaluer si la séquence contient exactement un élément, deux éléments au plus doivent être énumérés.

Exemple

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

  • Si la séquence contient exactement un élément ou exactement un élément correspondant au predicate fourni, cet élément est renvoyé.
  • Si la séquence ne contient aucun élément ou aucun élément correspondant au predicate fourni, la default(T) est renvoyée.
  • Si la séquence contient plusieurs éléments ou plusieurs éléments correspondant au predicate fourni, une InvalidOperationException est lancée avec le message "La séquence contient plusieurs éléments".
  • Si la séquence ne contient aucun élément correspondant au predicate fourni, retourne la valeur par défaut du type de séquence en utilisant la default(T) par default(T) .
  • Remarque: pour évaluer si la séquence contient exactement un élément, deux éléments au plus doivent être énumérés.

Exemple

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

Recommandations

  • Bien que vous puissiez utiliser FirstOrDefault , LastOrDefault ou SingleOrDefault pour vérifier si une séquence contient des éléments, Any ou Count sont plus fiables. En effet, une valeur de retour default(T) de l'une de ces trois méthodes ne prouve pas que la séquence est vide, car la valeur du premier / dernier / seul élément de la séquence pourrait également être default(T)

  • Décidez des méthodes qui correspondent le mieux au but de votre code. Par exemple, utilisez Single uniquement si vous devez vous assurer qu’un seul élément de la collection correspond à votre prédicat - sinon utilisez First ; comme Single lance une exception si la séquence a plus d'un élément correspondant. Ceci s'applique bien entendu aux partenaires "* OrDefault".

  • En ce qui concerne l’efficacité: Bien qu’il soit souvent approprié de s’assurer qu’il n’ya qu’un seul élément ( Single ) ou qu’un seul élément ou un seul SingleOrDefault ( SingleOrDefault ) renvoyé par une requête, ces deux méthodes requièrent plus, et souvent la totalité de la collection. à examiner pour s'assurer qu'il n'y a pas de seconde correspondance à la requête. Ceci est différent du comportement, par exemple, de la méthode First , qui peut être satisfaite après avoir trouvé la première correspondance.

Sauf

La méthode Except renvoie l'ensemble des éléments contenus dans la première collection mais ne sont pas contenus dans la seconde. IEqualityComparer par défaut est utilisé pour comparer les éléments dans les deux ensembles. Il y a une surcharge qui accepte un IEqualityComparer comme argument.

Exemple:

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

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

Sortie:

1
4

Démo en direct sur .NET Fiddle

Dans ce cas, .Except(second) exclut les éléments contenus dans le tableau second , à savoir 2 et 3 (0 et 5 ne sont pas contenus dans le first tableau et sont ignorés).

Notez que Except implique Distinct (c.-à-d. Qu'il supprime les éléments répétés). Par exemple:

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

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

Sortie:

1
4

Démo en direct sur .NET Fiddle

Dans ce cas, les éléments 1 et 4 ne sont renvoyés qu’une seule fois.


Implémenter IEquatable ou fournir la fonction IEqualityComparer permettra d'utiliser une méthode différente pour comparer les éléments. Notez que la méthode GetHashCode doit également être remplacée afin de renvoyer un code de hachage identique pour un object identique selon l'implémentation IEquatable .

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

Sortie:

Hanoukka

Démo en direct sur .NET Fiddle

SelectMany: Aplatir une séquence de séquences

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 }

Utilisez SelectMany() si vous en avez, ou vous créez une séquence de séquences, mais vous voulez que le résultat soit une longue séquence.

Dans la syntaxe de requête LINQ:

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

Si vous avez une collection de collections et que vous souhaitez pouvoir travailler sur les données de la collection parent et enfant en même temps, cela est également possible avec SelectMany .

Définissons des classes simples

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

Supposons que nous avons la collection suivante.

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

Nous souhaitons maintenant sélectionner les commentaires Content avec l' Id de BlogPost associé à ce commentaire. Pour ce faire, nous pouvons utiliser la surcharge SelectMany appropriée.

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

Nos commentsWithIds ressemble à ceci

{
    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

La méthode linq SelectMany «aplatit» un IEnumerable<IEnumerable<T>> en un IEnumerable<T> . Tous les éléments T des instances IEnumerable contenus dans la source IEnumerable seront combinés en un seul IEnumerable .

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

Si vous utilisez une fonction de sélecteur qui transforme les éléments d'entrée en séquences, le résultat sera les éléments de ces séquences renvoyés un par un.

Notez que, contrairement à Select() , le nombre d'éléments dans la sortie ne doit pas nécessairement être identique à celui de l'entrée.

Plus exemple du monde réel

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

Sortie:

Bob
Jack
Jim
John

Démo en direct sur .NET Fiddle

Tout

All est utilisé pour vérifier si tous les éléments d'une collection correspondent à une condition ou non.
voir aussi: .Tout

1. paramètre vide

Tout : ne peut pas être utilisé avec un paramètre vide.

2. Expression Lambda en tant que paramètre

All : Renvoie true si tous les éléments de la collection satisfont l'expression lambda et false sinon:

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. Collection vide

All : Renvoie true si la collection est vide et qu'une expression lambda est fournie:

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

Remarque: All arrêteront l'itération de la collection dès qu'elle trouvera un élément ne correspondant pas à la condition. Cela signifie que la collection ne sera pas nécessairement entièrement énumérée; il sera seulement énuméré assez loin pour trouver le premier élément ne correspondant pas à la condition.

Collection de requêtes par type / éléments de distribution à taper

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

Utiliser 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

En utilisant 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

En utilisant la 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 

syndicat

Fusionne deux collections pour créer une collection distincte à l'aide du comparateur d'égalité par défaut

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

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

Démo en direct sur .NET Fiddle

JOINT

Les jointures permettent de combiner différentes listes ou tables contenant des données via une clé commune.

Comme dans SQL, les types de jointures suivants sont pris en charge dans LINQ:
Jointures internes , gauche, droite, croisées et complètes .

Les deux listes suivantes sont utilisées dans les exemples ci-dessous:

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

(Jointure interne

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

Jointure externe gauche

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

Full Outer Join

var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);

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

Exemple pratique

Les exemples ci-dessus ont une structure de données simple afin que vous puissiez vous concentrer sur la compréhension technique des différentes jointures LINQ, mais dans le monde réel, vous aurez des tables avec des colonnes à joindre.

Dans l'exemple suivant, il n'y a qu'une classe Region utilisée. En réalité, vous rejoindrez plusieurs tables différentes contenant la même clé (dans cet exemple, la first et la second sont jointes via l' ID clé commune).

Exemple: considérez la structure de données suivante:

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

Maintenant, préparez les données (c.-à-d. Remplissez les données):

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

Vous pouvez voir que dans cet exemple, la first ne contient aucune description de région et que vous souhaitez donc les rejoindre depuis la second . Alors la jointure interne ressemblerait à:

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

Ce résultat a créé des objets anonymes à la volée, ce qui est bien, mais nous avons déjà créé une classe appropriée - nous pouvons donc le spécifier: au lieu de select new { f.ID, s.RegionDescription }; on peut dire select new Region(f.ID, s.RegionDescription); , qui renverra les mêmes données mais créera des objets de type Region - qui maintiendront la compatibilité avec les autres objets.

Démo en direct sur le violon .NET

Distinct

Renvoie les valeurs uniques d'un IEnumerable . L'unicité est déterminée à l'aide du comparateur d'égalité par défaut.

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

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

Pour comparer un type de données personnalisé, nous devons implémenter l' IEquatable<T> et fournir des méthodes GetHashCode et Equals pour le type. Ou le comparateur d'égalité peut être remplacé:

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 un ou plusieurs champs

Supposons que nous avons un modèle de film:

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

Propriété Group by Category:

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

Grouper par catégorie et année:

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

Utiliser Range avec différentes méthodes Linq

Vous pouvez utiliser la classe Enumerable aux côtés des requêtes Linq pour convertir les boucles en lignes Linq one.

Sélectionnez un exemple

Opposé à faire ceci:

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

Tu peux le faire:

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

Où exemple

Dans cet exemple, 100 numéros seront générés et même ceux qui seront extraits

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

Ordre des requêtes - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()

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

Ascendant:

Syntaxe de requête

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

Syntaxe de la méthode

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

namedNames contient les noms dans l'ordre suivant: "adam", "mark", "steve"

Descendant:

Syntaxe de requête

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

Syntaxe de la méthode

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

namedNames contient les noms dans l'ordre suivant: "steve", "mark", "adam"

Commander par plusieurs champs

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

Syntaxe de requête

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

Syntaxe de la méthode

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

Résultat

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

Les bases

LINQ est largement bénéfique pour interroger des collections (ou des tableaux).

Par exemple, compte tenu des exemples de données suivants:

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

Nous pouvons "interroger" ces données en utilisant la syntaxe LINQ. Par exemple, pour récupérer tous les élèves qui ont une collation aujourd'hui:

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

Ou, pour récupérer les élèves ayant une note de 90 ou plus et ne renvoyer que leurs noms, pas l'objet complet de l' Student :

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

La fonctionnalité LINQ est composée de deux syntaxes qui remplissent les mêmes fonctions, ont des performances presque identiques, mais sont écrites de manière très différente. La syntaxe dans l'exemple ci-dessus est appelée syntaxe de requête . L'exemple suivant illustre toutefois la syntaxe de la méthode . Les mêmes données seront renvoyées comme dans l'exemple ci-dessus, mais la façon dont la requête est écrite est différente.

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

Par groupe

GroupBy est un moyen simple de trier une collection d'éléments IEnumerable<T> en groupes distincts.

Exemple simple

Dans ce premier exemple, nous nous retrouvons avec deux groupes, des articles pairs et impairs.

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

Exemple plus complexe

Prenons comme exemple le regroupement d'une liste de personnes par âge. Tout d'abord, nous allons créer un objet Person qui possède deux propriétés, Name et Age.

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

Ensuite, nous créons notre liste de personnes avec des noms et des âges différents.

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

Ensuite, nous créons une requête LINQ pour regrouper notre liste de personnes par âge.

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

En faisant cela, nous pouvons voir l’âge pour chaque groupe et avoir une liste de chaque personne du groupe.

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

Cela se traduit par la sortie suivante:

20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith

Vous pouvez jouer avec la démo en direct sur .NET Fiddle

Tout

Any est utilisé pour vérifier si un élément d'une collection correspond à une condition ou non.
voir aussi: .All , Any et FirstOrDefault: meilleures pratiques

1. paramètre vide

Any : Renvoie true si la collection contient des éléments et false si la collection est vide:

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. Expression Lambda en tant que paramètre

Any : Renvoie true si la collection comporte un ou plusieurs éléments répondant à la condition de l'expression lambda:

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

3. Collection vide

Any : renvoie false si la collection est vide et qu'une expression lambda est fournie:

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

Remarque: Any arrête l'itération de la collection dès qu'il détecte un élément correspondant à la condition. Cela signifie que la collection ne sera pas nécessairement entièrement énumérée; il sera seulement énuméré assez loin pour trouver le premier article correspondant à la condition.

Démo en direct sur .NET Fiddle

ToDictionary

La ToDictionary() LINQ peut être utilisée pour générer une collection Dictionary<TKey, TElement> basée sur une source IEnumerable<T> donnée.

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

Dans cet exemple, le seul argument transmis à ToDictionary est de type Func<TSource, TKey> , qui renvoie la clé pour chaque élément.

C'est un moyen concis d'effectuer l'opération suivante:

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

Vous pouvez également transmettre un second paramètre à la méthode ToDictionary , qui est de type Func<TSource, TElement> et renvoie la Value à ajouter pour chaque entrée.

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

Il est également possible de spécifier l' IComparer utilisé pour comparer les valeurs clés. Cela peut être utile lorsque la clé est une chaîne et que vous souhaitez qu'elle corresponde à la casse.

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

Remarque: la méthode ToDictionary nécessite que toutes les clés soient uniques, il ne doit y avoir aucune clé en double. Si tel est le cas, une exception est levée: ArgumentException: An item with the same key has already been added. Si vous avez un scénario où vous savez que vous aurez plusieurs éléments avec la même clé, il vaut mieux utiliser ToLookup place.

Agrégat

Aggregate Applique une fonction d'accumulateur sur une séquence.

int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
  • Au premier pas prevSum = 1
  • Au second prevSum = prevSum(at the first step) + 2
  • A la iième étape 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 !"

Une deuxième surcharge de l' Aggregate reçoit également un paramètre d' seed qui correspond à la valeur initiale de l'accumulateur. Cela peut être utilisé pour calculer plusieurs conditions sur une collection sans l'itérer plus d'une fois.

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

Pour la collection d' items nous voulons calculer

  1. Le total .Count
  2. La quantité de nombres pairs
  3. Ramassez chaque article

En utilisant Aggregate cela peut se faire comme ceci:

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]

Notez que si vous utilisez un type anonyme comme graine, vous devez instancier un nouvel objet à chaque élément car les propriétés sont en lecture seule. En utilisant une classe personnalisée peut simplement affecter les informations et aucune new est nécessaire (seulement en donnant la première seed paramètre

Définir une variable dans une requête Linq (mot clé let)

Afin de définir une variable dans une expression linq, vous pouvez utiliser le mot - clé let . Ceci est généralement fait pour stocker les résultats des sous-requêtes intermédiaires, par exemple:

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

Sortie:

La moyenne des nombres est de 4,5.
Le résultat de la requête inclut le numéro 3 avec un carré de 9.
Le résultat de la requête inclut le numéro 4 avec un carré de 16.
Le résultat de la requête inclut le numéro 5 avec un carré de 25.
Le résultat de la requête inclut le numéro 6 avec un carré de 36.
Le résultat de la requête inclut le numéro 7 avec un carré de 49.
Le résultat de la requête inclut le numéro 8 avec un carré de 64.
Le résultat de la requête inclut le numéro 9 avec un carré de 81.

Voir la démo

SkipWhile

SkipWhile() est utilisé pour exclure des éléments jusqu'à la première non-correspondance (cela peut être contre-intuitif pour la plupart)

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

DefaultIfEmpty

DefaultIfEmpty est utilisé pour renvoyer un élément par défaut si la séquence ne contient aucun élément. Cet élément peut être la valeur par défaut du type ou une instance définie par l'utilisateur de ce type. Exemple:

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;

Utilisation dans les jointures à gauche :

Avec DefaultIfEmpty la jointure Linq traditionnelle peut renvoyer un objet par défaut si aucune correspondance n'a été trouvée. Agissant ainsi comme une jointure de gauche de SQL. Exemple:

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

Dans le cas où un DefaultIfEmpty est utilisé (sans spécifier de valeur par défaut) et qu'il en résultera aucun élément correspondant sur la bonne séquence, il faut s'assurer que l'objet n'est pas null avant d'accéder à ses propriétés. Sinon, cela entraînera une NullReferenceException . Exemple:

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

SéquenceEqual

SequenceEqual est utilisé pour comparer deux séquences IEnumerable<T> entre elles.

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

Count renvoie le nombre d'éléments dans un IEnumerable<T> . Count expose également un paramètre de prédicat facultatif qui vous permet de filtrer les éléments à compter.

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 fonctionne de la même manière que Count mais a un type de retour long et est utilisé pour compter les séquences IEnumerable<T> plus longues que 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

Créer progressivement une requête

Comme LINQ utilise une exécution différée , nous pouvons avoir un objet de requête qui ne contient pas réellement les valeurs, mais qui renverra les valeurs lors de l'évaluation. Nous pouvons donc construire dynamiquement la requête en fonction de notre flux de contrôle et l'évaluer une fois que nous avons terminé:

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

Nous pouvons appliquer des filtres conditionnellement:

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

Nous pouvons ajouter un ordre de tri à la requête en fonction d'une condition:

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

Notre requête peut être définie pour commencer à partir d'un point donné:

    query = query.Skip(start - 1);

et défini pour renvoyer un nombre spécifique d'enregistrements:

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

Une fois que nous avons l'objet de requête, nous pouvons évaluer les résultats avec une boucle foreach ou une des méthodes LINQ qui renvoie un ensemble de valeurs, telles que ToList ou ToArray :

SearchModel sm;

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

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

Zip *: français

La méthode d'extension Zip agit sur deux collections. Il associe chaque élément des deux séries en fonction de leur position. Avec une instance Func , nous utilisons Zip pour gérer les éléments des deux collections C # par paires. Si la taille de la série diffère, les éléments supplémentaires de la plus grande série seront ignorés.

Pour prendre un exemple du livre "C # in a Nutshell",

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

Sortie:

3 = trois
5 = cinq
7 = sept

Voir la démo

GroupJoin avec variable de plage externe

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

ElementAt retournera l'élément à l'index n . Si n n'est pas dans la plage de l'énumérable, lève une ArgumentOutOfRangeException .

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

ElementAtOrDefault renverra l'élément à l'index n . Si n n'est pas dans la plage de l'énumérable, retourne une default(T) par default(T) .

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

ElementAt et ElementAtOrDefault sont tous deux optimisés lorsque la source est un IList<T> et que l'indexation normale sera utilisée dans ces cas.

Notez que pour ElementAt , si l'index fourni est supérieur à la taille de IList<T> , la liste doit (mais n'est techniquement pas garantie) lancer une ArgumentOutOfRangeException .

Linq Quantifiers

Les opérations quantificateur renvoient une valeur booléenne si certains ou tous les éléments d'une séquence satisfont à une condition. Dans cet article, nous verrons quelques scénarios LINQ to Objects courants dans lesquels nous pouvons utiliser ces opérateurs. Il y a 3 opérations de quantificateurs qui peuvent être utilisées dans LINQ:

All - utilisé pour déterminer si tous les éléments d'une séquence satisfont à une condition. Par exemple:

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 - utilisé pour déterminer si des éléments d'une séquence satisfont à une condition. Par exemple:

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

Contains - utilisé pour déterminer si une séquence contient un élément spécifié. Par exemple:

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

Joindre plusieurs séquences

Considérez les entités Customer , Purchase et PurchaseItem comme suit:

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

Envisagez de suivre les exemples de données pour les entités ci-dessus:

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

Maintenant, considérez ci-dessous la requête linq:

var result = from c in customers
            join p in purchases on c.Id equals p.CustomerId           // first join
            join pi in purchaseItems on p.Id equals pi.PurchaseId     // second join
            select new
            {
               c.Name, p.Description, pi.Detail
            };

Pour afficher le résultat de la requête ci-dessus:

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

La sortie de la requête serait:

Client1, Client1-Achat1, Achat1-AchatItem1

Client1, Client1-Achat2, Achat2-AchatItem1

Client1, Client1-Achat2, Achat2-AchatItem2

Client2, Client2-Achat2, Achat3-AchatItem1

Démo en direct sur .NET Fiddle

Se joindre à plusieurs clés

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

Notez que les types anonymes dans la join ci-dessus doivent contenir les mêmes propriétés, car les objets sont considérés égaux uniquement si toutes leurs propriétés sont égales. Sinon, la requête ne sera pas compilée.

Sélectionnez avec Func sélecteur - Permet d'obtenir le classement des éléments

Sur les surcharges des méthodes d’extension Select , l’ index de l’élément actuel de la collection select transmis. Ce sont quelques utilisations.

Récupère le "numéro de ligne" des articles

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

Obtenez le rang d'un article dans son groupe

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

Obtenez le classement des groupes (également connu sous Oracle sous le nom 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();

Pour tester cela, vous pouvez utiliser:

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

Et des données:

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 renvoie des éléments d'une séquence tant que la condition est vraie

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

Somme

La méthode d'extension Enumerable.Sum calcule la somme des valeurs numériques.

Si les éléments de la collection sont eux-mêmes des nombres, vous pouvez calculer directement la somme.

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

Si le type des éléments est un type complexe, vous pouvez utiliser une expression lambda pour spécifier la valeur à calculer:

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

La méthode d'extension de somme peut calculer avec les types suivants:

  • Int32
  • Int64
  • Unique
  • Double
  • Décimal

Si votre collection contient des types nullables, vous pouvez utiliser l'opérateur null-coalescing pour définir une valeur par défaut pour les éléments null:

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

Pour rechercher

ToLookup renvoie une structure de données permettant l'indexation. C'est une méthode d'extension. Il produit une instance ILookup qui peut être indexée ou énumérée à l'aide d'une boucle foreach. Les entrées sont combinées en groupes à chaque touche. - dotnetperls

string[] array = { "one", "two", "three" };
//create lookup using string length as key
var lookup = array.ToLookup(item => item.Length);

//join the values whose lengths are 3
Console.WriteLine(string.Join(",",lookup[3]));
//output: one,two

Un autre exemple:

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

Construisez vos propres opérateurs Linq pour IEnumerable

L'un des avantages de Linq est qu'il est si facile à étendre. Il vous suffit de créer une méthode d'extension dont l'argument est 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;
        }
    }
}

Cet exemple divise les éléments dans un IEnumerable<T> en listes d'une taille fixe, la dernière liste contenant le reste des éléments. Notez que l'objet auquel la méthode d'extension est appliquée est passé (argument source ) comme argument initial à l'aide du mot this clé this . Ensuite, le mot-clé yield est utilisé pour générer l'élément suivant dans la sortie IEnumerable<T> avant de poursuivre l'exécution à partir de ce point (voir le mot-clé yield ).

Cet exemple serait utilisé dans votre code comme ceci:

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

Sur la première boucle, la sous-liste serait {2, 3, 4} et la seconde {5, 6} .

Les méthodes LinQ personnalisées peuvent également être combinées avec les méthodes LinQ standard. par exemple:

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

Cette requête renvoie des nombres pairs regroupés en lots de taille 3: {0, 2, 4}, {6, 8, 10}, {12}

N'oubliez pas que vous avez besoin de using MyNamespace; ligne pour pouvoir accéder à la méthode d'extension.

Utilisation de SelectMany au lieu de boucles imbriquées

2 listes données

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

si vous voulez sortir toutes les permutations, vous pouvez utiliser des boucles imbriquées comme

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

En utilisant SelectMany, vous pouvez effectuer la même opération que

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

Any and First (OrDefault) - meilleures pratiques

Je n'expliquerai pas ce que font Any et FirstOrDefault car il y a déjà deux bons exemples à leur sujet. Voir Any et First, FirstOrDefault, Last, LastOrDefault, Single et SingleOrDefault pour plus d'informations.

Un modèle que je vois souvent dans le code qui devrait être évité est

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

Cela pourrait être écrit plus efficacement comme ça

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

En utilisant le deuxième exemple, la collection est recherchée une seule fois et donne le même résultat que le premier. La même idée peut être appliquée à Single .

GroupBy Sum et Count

Prenons un exemple de classe:

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

Considérons maintenant une liste de transactions:

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

Si vous voulez calculer la somme sage de la somme et le nombre, vous pouvez utiliser GroupBy comme suit:

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

Sinon, vous pouvez le faire en une seule étape:

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

La sortie pour les deux requêtes ci-dessus serait identique:

Catégorie: Compte d'épargne, Montant: 66, Nombre: 2

Catégorie: Carte de crédit, montant: 71, nombre: 2

Catégorie: Compte courant, montant: 100, compte: 1

Démo en direct dans .NET Fiddle

Sens inverse

  • Inverse l'ordre des éléments dans une séquence.
  • S'il n'y a pas d'éléments lève une ArgumentNullException: source is null.

Exemple:

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

Exemple de code en direct

Rappelez-vous que Reverse() peut fonctionner différemment en fonction de l'ordre des chaînes de vos déclarations LINQ.

        //Create List of chars
        List<int> integerlist = new List<int>() { 1, 2, 3, 4, 5, 6 };

        //Reversing the list then taking the two first elements
        IEnumerable<int> reverseFirst = integerlist.Reverse<int>().Take(2);
        
        //Taking 2 elements and then reversing only thos two
        IEnumerable<int> reverseLast = integerlist.Take(2).Reverse();
        
        //reverseFirst output: 6, 5
        //reverseLast output:  2, 1

Exemple de code en direct

Reverse () fonctionne en tamponnant tout, puis marche en arrière, ce qui n'est pas très efficace, mais OrderBy ne l'est pas non plus.

Dans LINQ-to-Objects, il existe des opérations de mise en mémoire tampon (Reverse, OrderBy, GroupBy, etc.) et des opérations sans mise en mémoire tampon (Where, Take, Skip, etc.).

Exemple: extension inversée non tampon

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

Exemple de code en direct

Cette méthode peut rencontrer des problèmes si vous modifiez la liste pendant une itération.

Énumérer les Enumerables

L'interface IEnumerable <T> est l'interface de base pour tous les énumérateurs génériques et constitue une partie essentielle de la compréhension de LINQ. À la base, il représente la séquence.

Cette interface sous-jacente est héritée par toutes les collections génériques, telles que Collection <T> , Array , List <T> , Dictionary <TKey, TValue> Class et HashSet <T> .

En plus de représenter la séquence, toute classe qui hérite de IEnumerable <T> doit fournir un IEnumerator <T>. L'énumérateur expose l'itérateur à l'énumérable, et ces deux interfaces et idées interconnectées sont à l'origine du dicton "énumérer les énumérables".

"Enumerating the enumerable" est une phrase importante. L'énumérateur est simplement une structure pour savoir comment itérer, il ne contient aucun objet matérialisé. Par exemple, lors du tri, un énumérable peut contenir les critères du champ à trier, mais l'utilisation de .OrderBy() en lui-même renverra un IEnumerable <T> qui ne sait que trier. L'utilisation d'un appel qui matérialisera les objets, comme dans l'itération de l'ensemble, est appelée énumération (par exemple .ToList() ). Le processus d'énumération utilisera la définition énumérable de la manière de parcourir les séries et de renvoyer les objets pertinents (dans l'ordre, filtrés, projetés, etc.).

Ce n’est qu’une fois que l’énumérable a été énuméré que cela provoque la matérialisation des objets, c’est-à-dire lorsque des métriques telles que la complexité temporelle (durée liée à la taille des séries) et la complexité spatiale (quantité Être mesuré.

Créer votre propre classe qui hérite de IEnumerable <T> peut être un peu compliqué en fonction de la série sous-jacente qui doit être énumérée. En général, il est préférable d'utiliser l'une des collections génériques existantes. Cela dit, il est également possible d'hériter de l'interface IEnumerable <T> sans avoir un tableau défini comme structure sous-jacente.

Par exemple, utiliser la série Fibonacci comme séquence sous-jacente. Notez que l'appel à Where construit simplement un IEnumerable , et ce n'est qu'un appel à énumérer que énumérable est fait que l'une des valeurs est matérialisée.

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

Sortie

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

La force du second ensemble (le fibMod612) est que même si nous avons appelé pour ordonner l’ensemble de nos nombres de Fibonacci, une seule valeur ayant été prise avec .First() la complexité du temps était O (n) comme une seule valeur. besoin d'être comparé lors de l'exécution de l'algorithme de commande. Ceci est dû au fait que notre enquêteur n'a demandé qu'une valeur, de sorte que l'intégralité de l'énumérateur n'a pas à être matérialisée. Si nous avions utilisé .Take(5) au lieu de .First() l'énumérateur aurait demandé 5 valeurs et au plus 5 valeurs devraient être matérialisées. Par rapport à la nécessité de commander un ensemble complet et de prendre ensuite les 5 premières valeurs, le principe d'économiser beaucoup de temps d'exécution et d'espace.

Commandé par

Ordonne une collection par une valeur spécifiée.

Lorsque la valeur est un entier , double ou float, elle commence par la valeur minimale , ce qui signifie que vous obtenez d'abord les valeurs négatives, puis zéro et les mots après les valeurs positives (voir l'exemple 1).

Lorsque vous commandez un ombles la méthode compare les valeurs ascii des caractères pour trier la collection (voir l' exemple 2).

Lorsque vous triez des chaînes, la méthode OrderBy les compare en examinant CultureInfo, mais en commençant normalement par la première lettre de l'alphabet (a, b, c ...).

Ce type d'ordre est appelé ascendant, si vous voulez qu'il en soit autrement (voir OrderByDescending).

Exemple 1:

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

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

Exemple:

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

Ordonne une collection par une valeur spécifiée.

Lorsque la valeur est un entier , double ou float, elle commence par la valeur maximale , ce qui signifie que vous obtenez d'abord les valeurs positives, et zéro les valeurs négatives (voir l'exemple 1).

Lorsque vous commandez un ombles la méthode compare les valeurs ascii des caractères pour trier la collection (voir l' exemple 2).

Lorsque vous triez des chaînes, la méthode OrderBy les compare en examinant CultureInfo, mais en commençant normalement par la dernière lettre de l'alphabet (z, y, x, ...).

Ce type d'ordre est appelé décroissant, si vous voulez l'inverse, vous devez l'escalader (voir OrderBy).

Exemple 1:

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

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

Exemple 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

Fusionne deux collections (sans supprimer les doublons)

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

Contient

MSDN:

Détermine si une séquence contient un élément spécifié à l'aide d'un IEqualityComparer<T> spécifié

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

En utilisant un objet défini par l'utilisateur:

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

Utilisation de la 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

Une utilisation judicieuse de Contains serait de remplacer plusieurs clauses if par un appel Contains .

Donc, au lieu de faire ceci:

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

Faites ceci:

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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow