CodeGym /Cours /C# SELF /Projection de données avec

Projection de données avec Select

C# SELF
Niveau 31 , Leçon 3
Disponible

1. Intro

Quand tu mates une série, t'as pas toujours toutes les infos sur tous les persos, juste ce qui sert à l'histoire. En prog, c'est pareil : souvent t'as pas besoin de tout l'objet, juste de certains champs, valeurs ou même des calculs dessus.

Projection dans LINQ — c'est transformer les éléments d'une séquence de base en une nouvelle forme. D'habitude avec la méthode Select. Tu peux voir Select comme une machine magique qui prend chaque élément, applique une fonction dessus et te renvoie le résultat. C'est tout, pas de magie noire.

Quand t'as besoin d'une projection ?

  • Tu veux afficher juste les noms des users, pas toutes leurs infos.
  • Tu prépares une newsletter : tu prends juste l'adresse et le nom du client.
  • Tu fais un calcul : tu ajoutes la taxe au prix d'un produit et tu récupères une nouvelle collection.
  • Pour l'UI : tu veux afficher qu'une partie des données, pas surcharger l'interface.

2. La méthode Select : syntaxe et base

La syntaxe de base

LINQ gère deux styles : Method Syntax (style méthode) et Query Syntax (style requête). Pour Select, le résultat est le même dans les deux.

Syntaxe méthode

var query = myCollection.Select(x => x.QuoiRetourner);

Ici x — c'est la variable pour chaque élément de la collection, et à droite — l'expression qui transforme l'élément en ce que tu veux.

Syntaxe requête

var query = from x in myCollection
            select x.QuoiRetourner;

Là ça ressemble un peu plus à du SQL.

Exemple simple : une liste de nombres

var numbers = new List<int> { 1, 2, 3, 4, 5 };
// Je veux leurs carrés
var squares = numbers.Select(n => n * n);

foreach (var s in squares)
{
    Console.WriteLine(s); // 1, 4, 9, 16, 25
}

Voilà, on a transformé une collection de nombres en une collection de leurs carrés — direct, sans boucle manuelle !

Exemple 2

Dans les exemples d'avant, on avait déjà une classe Product :

public class Product
{
    public string Name { get; set; }
    public double Price { get; set; }
}

Imaginons qu'on a une liste :

var products = new List<Product>
{
    new Product { Name = "Chocolat", Price = 120 },
    new Product { Name = "Fromage", Price = 250 },
    new Product { Name = "Pain", Price = 55 }
};

Si tu veux juste les noms de tous les produits, t'as pas besoin de tout l'objet, non ? Juste la liste des noms :

var names = products.Select(p => p.Name);
foreach (var name in names)
{
    Console.WriteLine(name); // Chocolat, Fromage, Pain
}

3. La collection retournée — c'est quoi les options ?

Fais gaffe, Select renvoie toujours une collection (plus précisément — IEnumerable<TOutput>), où TOutput — c'est le type du résultat de ta fonction. C'est toi qui choisis : string, nombre, type anonyme, même un nouvel objet.

Projection vers un nouveau type

Par exemple, tu veux pas l'objet brut, mais sa "projection" :

var projections = products.Select(p => new { p.Name, PrixTTC = p.Price * 1.2 });

foreach (var item in projections)
{
    Console.WriteLine($"{item.Name}: {item.PrixTTC}");
}

Là on a créé une nouvelle collection d'objets anonymes — avec le nom et le prix avec une TVA imaginaire !

4. Retourner une nouvelle classe ou un type anonyme

Tu peux choisir : créer des vraies classes pour le résultat ou juste des types anonymes si la structure sert juste "ici et maintenant".

Types anonymes

var result = products.Select(p => new { Nom = p.Name, AncienPrix = p.Price, NouveauPrix = p.Price + 40 });
foreach (var p in result)
{
    Console.WriteLine($"{p.Nom}: Avant {p.AncienPrix}, maintenant {p.NouveauPrix}");
}

Les types anonymes, c'est pratique pour générer vite fait des résultats, surtout quand ça sert à rien de créer une classe juste pour une opération.

Utiliser ta propre classe pour la projection

On crée une nouvelle classe :

public class ProductDto
{
    public string Name { get; set; }
    public double PriceWithTax { get; set; }
}

Maintenant dans la requête LINQ :

var productsWithTax = products.Select(p => new ProductDto
{
    Name = p.Name,
    PriceWithTax = p.Price * 1.2
});

foreach (var p in productsWithTax)
{
    Console.WriteLine($"{p.Name}: {p.PriceWithTax}");
}

5. Accès aux collections internes et propriétés

Et si t'as une collection dans l'objet ? Genre chaque user a une liste d'achats.

public class User
{
    public string Name { get; set; }
    public List<Product> Purchases { get; set; }
}

Tu veux la liste de toutes les listes d'achats ? Easy !

var allPurchases = users.Select(u => u.Purchases);

foreach (var purchaseList in allPurchases)
{
    foreach (var product in purchaseList)
    {
        Console.WriteLine(product.Name);
    }
}

Important ! Là t'as pas une liste "plate" de produits, mais une collection de collections. Comment avoir UNE seule liste avec tous les produits de tous les users ? Ce sera pour la prochaine conf — on va voir SelectMany.

6. Astuces utiles

Conversion en type de collection : ToList() et cie

Select te renvoie un IEnumerable<T>. Si tu veux une liste classique (List<T>), ajoute juste .ToList() :

var nameList = products.Select(p => p.Name).ToList();

Maintenant t'as une vraie liste, tu peux bosser dessus comme d'hab.

Utiliser l'index de l'élément

Parfois c'est cool de savoir à quel rang dans la collection t'es. La méthode Select a une surcharge :

var indexed = products.Select((product, index) => new { Index = index, Name = product.Name });
foreach (var item in indexed)
{
    Console.WriteLine($"{item.Index}: {item.Name}");
}

Comme ça tu peux préparer des listes numérotées, par exemple.

Projection en prog

Maîtriser Select — c'est une question classique en entretien. Sans ça, dur d'imaginer une appli moderne :

  • Récupérer juste les données utiles de la BDD ou d'un service externe.
  • Préparer les données à passer entre les couches de l'appli.
  • Transformer les données pour l'affichage ou l'envoi réseau.
  • Économiser de la RAM (tu prends pas tout l'objet, juste les champs utiles).

Les boîtes qui gèrent beaucoup de données basent souvent leur logique métier là-dessus.

7. Erreurs classiques et particularités

Parfois les débutants tombent sur des bugs relous — souvent parce qu'ils pigent pas comment marche l'exécution paresseuse (deferred execution) dans LINQ. Par exemple, après avoir appelé Select, la requête s'exécute pas direct — c'est que quand tu parcours la collection (foreach, ToList(), etc.). Ça permet de faire un "pipeline de traitement", mais parfois le résultat change si les données de base ont changé avant d'utiliser le résultat.

N'oublie pas non plus que Select modifie pas la collection de base — il crée une nouvelle. Donc si tu t'attends à voir de nouveaux champs dans products après products.Select(...), ça arrivera pas.

Gestion des null : si ta projection touche des champs qui peuvent être null, pense à gérer ça. En C# 8+, t'auras un warning du compilateur si tu risques un NullReferenceException.

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION