Introduction à LINQ
LINQ (Language Integrated Query) est une technologie puissante qui permet d'interroger et de manipuler des collections de données avec une syntaxe élégante et intuitive, similaire à SQL.
Pourquoi utiliser LINQ ?
- Syntaxe claire : Code plus lisible et maintenable
- Type-safe : Vérification à la compilation
- Polyvalent : Fonctionne avec arrays, listes, dictionnaires, bases de données
- Puissant : Filtrage, tri, projection, agrégation en une ligne
Opérations de base avec LINQ
Where - Filtrage
Filtre les éléments selon une condition :
using System;
using System.Linq;
using System.Collections.Generic;
List<int> nombres = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var pairs = nombres.Where(n => n % 2 == 0);
Console.WriteLine("Nombres pairs:");
foreach (var nombre in pairs)
{
Console.Write($"{nombre} ");
}
var impairs = from n in nombres
where n % 2 != 0
select n;
Console.WriteLine("\nNombres impairs:");
foreach (var nombre in impairs)
{
Console.Write($"{nombre} ");
}
Select - Projection
Transforme chaque élément de la collection :
List<int> nombres = new List<int> { 1, 2, 3, 4, 5 };
var doubles = nombres.Select(n => n * 2);
Console.WriteLine(string.Join(", ", doubles));
var carres = nombres.Select(n => n * n);
Console.WriteLine(string.Join(", ", carres));
var formattes = nombres.Select(n => $"Numéro {n}");
foreach (var texte in formattes)
{
Console.WriteLine(texte);
}
OrderBy / OrderByDescending - Tri
List<string> noms = new List<string> { "Charlie", "Alice", "Bob", "David" };
var nomsTries = noms.OrderBy(n => n);
Console.WriteLine(string.Join(", ", nomsTries));
var nomsTriesDesc = noms.OrderByDescending(n => n);
Console.WriteLine(string.Join(", ", nomsTriesDesc));
var parLongueur = noms.OrderBy(n => n.Length).ThenBy(n => n);
Console.WriteLine(string.Join(", ", parLongueur));
Travail avec des objets complexes
public class Etudiant
{
public string Nom { get; set; }
public int Age { get; set; }
public double Moyenne { get; set; }
public string Specialite { get; set; }
}
List<Etudiant> etudiants = new List<Etudiant>
{
new Etudiant { Nom = "Alice", Age = 20, Moyenne = 15.5, Specialite = "Informatique" },
new Etudiant { Nom = "Bob", Age = 22, Moyenne = 12.0, Specialite = "Mathématiques" },
new Etudiant { Nom = "Charlie", Age = 21, Moyenne = 16.8, Specialite = "Informatique" },
new Etudiant { Nom = "Diana", Age = 19, Moyenne = 14.2, Specialite = "Physique" }
};
var bonnesMoyennes = etudiants.Where(e => e.Moyenne >= 14);
var classeParMoyenne = etudiants.OrderByDescending(e => e.Moyenne);
var nomsInfo = etudiants
.Where(e => e.Specialite == "Informatique")
.Select(e => e.Nom);
Console.WriteLine("Étudiants en informatique:");
Console.WriteLine(string.Join(", ", nomsInfo));
Méthodes d'agrégation
List<int> notes = new List<int> { 12, 15, 18, 10, 16, 14 };
int nombreNotes = notes.Count();
Console.WriteLine($"Nombre de notes: {nombreNotes}");
int somme = notes.Sum();
Console.WriteLine($"Somme: {somme}");
double moyenne = notes.Average();
Console.WriteLine($"Moyenne: {moyenne:F2}");
int maximum = notes.Max();
int minimum = notes.Min();
Console.WriteLine($"Min: {minimum}, Max: {maximum}");
int notesReussies = notes.Count(n => n >= 10);
Console.WriteLine($"Notes ≥ 10: {notesReussies}");
Méthodes de recherche
List<string> fruits = new List<string> { "Pomme", "Banane", "Orange", "Kiwi" };
string premierFruit = fruits.First();
Console.WriteLine($"Premier: {premierFruit}");
string fruitLong = fruits.FirstOrDefault(f => f.Length > 6);
Console.WriteLine($"Fruit > 6 lettres: {fruitLong}");
string dernierFruit = fruits.Last();
Console.WriteLine($"Dernier: {dernierFruit}");
bool contientOrange = fruits.Any(f => f == "Orange");
Console.WriteLine($"Contient Orange: {contientOrange}");
bool tousLongs = fruits.All(f => f.Length > 3);
Console.WriteLine($"Tous > 3 lettres: {tousLongs}");
bool contientPomme = fruits.Contains("Pomme");
Console.WriteLine($"Contient Pomme: {contientPomme}");
Opérations avancées
GroupBy - Regroupement
var parSpecialite = etudiants.GroupBy(e => e.Specialite);
foreach (var groupe in parSpecialite)
{
Console.WriteLine($"\n{groupe.Key}:");
foreach (var etudiant in groupe)
{
Console.WriteLine($" - {etudiant.Nom} ({etudiant.Moyenne})");
}
}
var comptesParSpecialite = etudiants
.GroupBy(e => e.Specialite)
.Select(g => new { Specialite = g.Key, Nombre = g.Count() });
foreach (var item in comptesParSpecialite)
{
Console.WriteLine($"{item.Specialite}: {item.Nombre} étudiant(s)");
}
Distinct - Éléments uniques
List<int> nombresAvecDoublons = new List<int> { 1, 2, 2, 3, 3, 3, 4, 5, 5 };
var nombresUniques = nombresAvecDoublons.Distinct();
Console.WriteLine(string.Join(", ", nombresUniques));
var specialitesUniques = etudiants.Select(e => e.Specialite).Distinct();
Console.WriteLine("Spécialités: " + string.Join(", ", specialitesUniques));
Take / Skip - Pagination
List<int> nombres = Enumerable.Range(1, 100).ToList();
var dixPremiers = nombres.Take(10);
Console.WriteLine("10 premiers: " + string.Join(", ", dixPremiers));
var page2 = nombres.Skip(10).Take(10);
Console.WriteLine("Page 2: " + string.Join(", ", page2));
int pageSize = 10;
int pageNumber = 3;
var page = nombres.Skip((pageNumber - 1) * pageSize).Take(pageSize);
Console.WriteLine($"Page {pageNumber}: " + string.Join(", ", page));
Évaluation différée (Lazy Evaluation) :
LINQ utilise l'évaluation différée : les requêtes ne sont exécutées que lorsque vous itérez sur les résultats. Pour forcer l'évaluation immédiate, utilisez :
- ToList() : Convertit en List<T>
- ToArray() : Convertit en tableau
- Count(), Sum(), etc. : Méthodes d'agrégation
Chaînage d'opérations
La vraie puissance de LINQ vient du chaînage d'opérations :
var top3Info = etudiants
.Where(e => e.Specialite == "Informatique")
.OrderByDescending(e => e.Moyenne)
.Take(3)
.Select(e => new { e.Nom, e.Moyenne });
Console.WriteLine("Top 3 en informatique:");
foreach (var etudiant in top3Info)
{
Console.WriteLine($"{etudiant.Nom}: {etudiant.Moyenne}/20");
}
var statistiques = etudiants
.Where(e => e.Moyenne >= 10)
.GroupBy(e => e.Specialite)
.Select(g => new
{
Specialite = g.Key,
NombreEtudiants = g.Count(),
MoyenneGenerale = g.Average(e => e.Moyenne),
MeilleureMoyenne = g.Max(e => e.Moyenne)
})
.OrderByDescending(s => s.MoyenneGenerale);
foreach (var stat in statistiques)
{
Console.WriteLine($"\n{stat.Specialite}:");
Console.WriteLine($" Étudiants: {stat.NombreEtudiants}");
Console.WriteLine($" Moyenne: {stat.MoyenneGenerale:F2}");
Console.WriteLine($" Meilleure note: {stat.MeilleureMoyenne}");
}
Exercice pratique
Exercice :
Créez un système d'analyse de ventes avec LINQ :
- Une classe Vente avec produit, montant, date, catégorie
- Générez des données de ventes aléatoires
- Calculez le total des ventes par catégorie
- Trouvez les 5 meilleures ventes
- Calculez la moyenne des ventes par mois
- Identifiez les produits les plus vendus
Système d'analyse de ventes avec LINQ
using System;
using System.Collections.Generic;
using System.Linq;
namespace AnalyseVentes
{
public class Vente
{
public int Id { get; set; }
public string Produit { get; set; }
public decimal Montant { get; set; }
public DateTime Date { get; set; }
public string Categorie { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("📊 === ANALYSE DES VENTES ===");
List<Vente> ventes = GenererVentes(100);
Console.WriteLine("\n💰 Total des ventes par catégorie:");
var totalParCategorie = ventes
.GroupBy(v => v.Categorie)
.Select(g => new
{
Categorie = g.Key,
Total = g.Sum(v => v.Montant),
NombreVentes = g.Count()
})
.OrderByDescending(x => x.Total);
foreach (var item in totalParCategorie)
{
Console.WriteLine($" {item.Categorie}: {item.Total:C} ({item.NombreVentes} ventes)");
}
Console.WriteLine("\n🏆 Top 5 des meilleures ventes:");
var top5Ventes = ventes
.OrderByDescending(v => v.Montant)
.Take(5);
int rang = 1;
foreach (var vente in top5Ventes)
{
Console.WriteLine($" {rang}. {vente.Produit} - {vente.Montant:C} ({vente.Date:dd/MM/yyyy})");
rang++;
}
Console.WriteLine("\n📅 Moyenne des ventes par mois:");
var moyenneParMois = ventes
.GroupBy(v => new { v.Date.Year, v.Date.Month })
.Select(g => new
{
Mois = new DateTime(g.Key.Year, g.Key.Month, 1),
Moyenne = g.Average(v => v.Montant),
NombreVentes = g.Count()
})
.OrderBy(x => x.Mois);
foreach (var item in moyenneParMois)
{
Console.WriteLine($" {item.Mois:MMMM yyyy}: {item.Moyenne:C} (sur {item.NombreVentes} ventes)");
}
Console.WriteLine("\n🛒 Top 5 produits les plus vendus:");
var produitsPopulaires = ventes
.GroupBy(v => v.Produit)
.Select(g => new
{
Produit = g.Key,
NombreVentes = g.Count(),
ChiffreAffaires = g.Sum(v => v.Montant)
})
.OrderByDescending(x => x.NombreVentes)
.Take(5);
rang = 1;
foreach (var produit in produitsPopulaires)
{
Console.WriteLine($" {rang}. {produit.Produit}: {produit.NombreVentes} ventes ({produit.ChiffreAffaires:C})");
rang++;
}
Console.WriteLine("\n📈 Statistiques globales:");
decimal totalVentes = ventes.Sum(v => v.Montant);
decimal moyenneVente = ventes.Average(v => v.Montant);
decimal venteMax = ventes.Max(v => v.Montant);
decimal venteMin = ventes.Min(v => v.Montant);
Console.WriteLine($" Nombre total de ventes: {ventes.Count}");
Console.WriteLine($" Chiffre d'affaires total: {totalVentes:C}");
Console.WriteLine($" Vente moyenne: {moyenneVente:C}");
Console.WriteLine($" Vente max: {venteMax:C}");
Console.WriteLine($" Vente min: {venteMin:C}");
Console.WriteLine("\n📊 Tendances:");
var ventesRecentes = ventes.Where(v => v.Date >= DateTime.Now.AddDays(-30));
var ventesAnciennes = ventes.Where(v => v.Date < DateTime.Now.AddDays(-30));
if (ventesRecentes.Any() && ventesAnciennes.Any())
{
decimal moyenneRecente = ventesRecentes.Average(v => v.Montant);
decimal moyenneAncienne = ventesAnciennes.Average(v => v.Montant);
decimal evolution = ((moyenneRecente - moyenneAncienne) / moyenneAncienne) * 100;
string tendance = evolution > 0 ? "📈 hausse" : "📉 baisse";
Console.WriteLine($" Évolution sur 30 jours: {tendance} de {Math.Abs(evolution):F1}%");
}
Console.WriteLine("\nAppuyez sur une touche pour quitter...");
Console.ReadKey();
}
static List<Vente> GenererVentes(int nombre)
{
Random random = new Random();
string[] produits = { "Laptop", "Souris", "Clavier", "Écran", "Casque", "Webcam", "Imprimante" };
string[] categories = { "Électronique", "Accessoires", "Périphériques" };
List<Vente> ventes = new List<Vente>();
for (int i = 0; i < nombre; i++)
{
ventes.Add(new Vente
{
Id = i + 1,
Produit = produits[random.Next(produits.Length)],
Montant = (decimal)(random.NextDouble() * 1000 + 10),
Date = DateTime.Now.AddDays(-random.Next(0, 90)),
Categorie = categories[random.Next(categories.Length)]
});
}
return ventes;
}
}
}
Points clés à retenir
- Where : Filtrer les éléments selon une condition
- Select : Transformer/projeter les éléments
- OrderBy/OrderByDescending : Trier les éléments
- Sum/Average/Min/Max : Opérations d'agrégation
- GroupBy : Regrouper les éléments par clé
- First/FirstOrDefault : Récupérer le premier élément
- Any/All : Vérifier les conditions sur la collection
- Take/Skip : Pagination des résultats
- ToList/ToArray : Matérialiser les résultats
Performance :
LINQ est puissant mais peut impacter les performances sur de très grandes collections. Pour des cas critiques, considérez les boucles traditionnelles ou les optimisations spécifiques.