CodeGym /Cours /C# SELF /Nouvelle méthode LINQ : In...

Nouvelle méthode LINQ : Index

C# SELF
Niveau 32 , Leçon 3
Disponible

1. L’évolution de la galère

T’as sûrement déjà vu ça : tu dois pas juste parcourir une collection, mais aussi connaître le numéro de chaque élément. Genre pour afficher une liste numérotée, modifier les éléments qui ont un index non nul ou faire un truc avec chaque troisième. En for classique — easy :

// Rien de nouveau, la base du C# :
for (int i = 0; i < list.Count; i++)
{
    Console.WriteLine($"{i}: {list[i]}");
}

Mais dès que tu commences à écrire en LINQ, là… tu perds l’index ! Tous ces jolis .Where, .Select, .OrderBy te filent que l’élément, pas son numéro. Bien sûr, t’aimerais faire un truc du genre :

list.SelectWithIndex((item, index) => ...);

Mais une méthode standard genre SelectWithIndex, ça n’a jamais existé. Ouais, tu peux choper l’index avec la surcharge de Select, mais… si tu veux juste l’index sans transformer ou projeter, tu dois bricoler avec des .Select en plus, et ça rend ton code LINQ moins clean.

Comment on survivait avant .NET 9

À l’époque, les devs C# avaient leurs petites astuces de hacker :

var result = list.Select((item, index) => new { item, index });

Et aussi des combos avec d’autres méthodes LINQ, mais c’était jamais vraiment “intégré”, et pas aussi joli qu’on voudrait.

2. Nouvelle méthode LINQ : Index — c’est quoi et pourquoi ?

Description officielle

Dans .NET 9, l’équipe de devs a entendu les pleurs de millions de programmeurs (bon, des dizaines de milliers sur Twitter, c’est déjà pas mal) — et a ajouté à LINQ une nouvelle méthode d’extension — Index. Sur la page officielle de la doc .NET 9 tu trouves ça :

Index() ajoute à chaque élément de la séquence son numéro d’ordre, à partir de zéro, et te renvoie des paires (valeur, index), sans avoir à créer explicitement des objets anonymes ou écrire .Select((item, idx) => new { item, idx }).

C’est puissant — maintenant Index te renvoie direct pour chaque élément une paire : l’élément et son index.

Signature de la méthode

public static IEnumerable<(T Element, int Index)> Index<T>(this IEnumerable<T> source);

En mode humain : Pour chaque élément de la collection, tu reçois un “tuple” spécial — Element et son Index. C’est tout. Plus besoin de bidouiller des types anonymes.

3. Exemples d’utilisation de la méthode Index

Un exemple ultra simple

On prend une liste de fruits préférés.

var fruits = new List<string> { "Pomme", "Banane", "Orange", "Kiwi" };

foreach (var (fruit, idx) in fruits.Index())
{
    Console.WriteLine($"{idx}: {fruit}");
}

Sortie :

0: Pomme
1: Banane
2: Orange
3: Kiwi

Voilà, c’est tout ! Maintenant tu peux choper la paire “index-valeur” et l’utiliser dans n’importe quelle requête LINQ.

Intégration avec les autres méthodes LINQ

Index — c’est un vrai membre de la famille LINQ ! Tu peux le mettre tranquille dans tes “chaînes”.

Exemple 1 : Filtrer les éléments d’index impair

var numbers = Enumerable.Range(10, 10); // 10, 11, ... 19
var oddIndexes = numbers.Index()
                        .Where(pair => pair.Index % 2 == 1)
                        .Select(pair => pair.Element);

Console.WriteLine(string.Join(", ", oddIndexes));

Sortie :

11, 13, 15, 17, 19

Exemple 2 : Modifier les éléments avec un index pair

var users = new List<string> { "Anna", "Igor", "Katya", "Denis" };
var modified = users.Index()
                    .Select(pair => pair.Index % 2 == 0 ? pair.Element.ToUpper() : pair.Element.ToLower());

foreach (var name in modified)
    Console.WriteLine(name);

Sortie :

ANNA
igor
KATYA
denis

Exemple 3 : Fusionner avec d’autres collections par index (style zip)

var ids = new[] { 101, 102, 103 };
var names = new[] { "Alice", "Bob", "Charlie" };

var merged = names.Index()
                  .Join(ids.Index(), 
                        namePair => namePair.Index,
                        idPair => idPair.Index,
                        (namePair, idPair) => (idPair.Element, namePair.Element));

foreach (var (id, name) in merged)
    Console.WriteLine($"{id}: {name}");

Sortie :

101: Alice
102: Bob
103: Charlie

4. Index dans une appli réelle

On va ajouter dans notre appli “éternelle” d’apprentissage une nouvelle fonctionnalité : afficher la liste de tous les étudiants de notre collection avec leur numéro d’ordre (genre pour que l’utilisateur puisse choisir un étudiant par numéro).

Exemple : Afficher la liste des étudiants avec les numéros

Imaginons qu’on a déjà la classe Student des exemples précédents :

public class Student
{
    public string Name { get; set; }
    public int Grade { get; set; }
}

On crée une petite liste d’étudiants :

var students = new List<Student>
{
    new Student { Name = "Dacha", Grade = 5 },
    new Student { Name = "Petia", Grade = 3 },
    new Student { Name = "Vova", Grade = 4 },
    new Student { Name = "Olia", Grade = 5 }
};

Maintenant, avec Index, on affiche tout ça avec les numéros :

foreach (var (student, idx) in students.Index())
{
    Console.WriteLine($"{idx + 1}. {student.Name} — Note : {student.Grade}");
}
Ici idx + 1 — pour que la numérotation commence à 1, pas à zéro.

Résultat :

1. Dacha — Note : 5
2. Petia — Note : 3
3. Vova — Note : 4
4. Olia — Note : 5

Utilité pratique : Maintenant, si l’utilisateur veut choisir un étudiant par numéro — c’est prêt ! Le code est plus simple, pas de compteurs “à la main”, max de lisibilité — min de bugs.

5. Comparaison : pourquoi Index défonce les vieux trucs ?

Avant .NET 9 : old school

Avant, pour avoir l’élément et son index, tu devais utiliser la surcharge .Select((item, index) => ...) et construire des types anonymes :

var withIndexes = students.Select((student, index) => new { student, index });

Pour choper les champs, tu devais toujours écrire .student, .index — et en plus c’est un type anonyme, pas de joli tuple nommé.

Avec .NET 9 : style XXIe siècle

Maintenant — plus besoin de se prendre la tête avec les champs, les types anonymes. C’est limpide :

foreach (var (student, idx) in students.Index())
{
    // ça marche direct, intuitif, simple, stylé
}

Le code est plus clean. Moins de code, moins d’erreurs. Parfait pour les grosses chaînes LINQ où t’as pas envie de penser aux surcharges en plus.

6. Détails et subtilités d’utilisation

Où tu peux l’utiliser

Index marche avec tout objet qui implémente IEnumerable<T>. Donc — toutes les collections classiques, tableaux, résultats d’autres requêtes LINQ.

Quel type retourne Index ?

Ça retourne une énumération de tuples, où le premier élément — c’est l’objet lui-même (souvent appelé Element), le deuxième — Index (type int). Grâce à la syntaxe moderne des tuples en C#, tu peux direct dans la boucle écrire foreach (var (element, index) in ...) et choper les deux valeurs dans tes variables.

On peut l’utiliser avec Query Syntax ?

Non, Index — c’est une méthode d’extension, la query syntax (LINQ façon SQL) ne la supporte pas direct. Donc comme ça, ça marche pas :

// Ça marche pas !
var query = from s in students.Index() select ...;

Si tu veux mixer, mets juste la méthode dans des parenthèses et bosse avec le résultat comme une collection normale :

var query = from pair in students.Index()
            where pair.Index > 1
            select pair.Element;

Indexation : toujours à partir de zéro

Index commence toujours à zéro, comme la plupart des trucs en prog C#. Si tu veux commencer à un, ajoute juste 1 où il faut.

8. Erreurs et subtilités — à quoi faire gaffe

Beaucoup d’étudiants au début confondent Index et la surcharge .Select((item, index) => ...). Les erreurs les plus courantes :

— Ils essaient d’utiliser Index en query syntax : “Pourquoi ça marche pas ?” — bah c’est que ça marche que comme méthode d’extension.

— Ils s’attendent à ce que l’index commence à 1, alors qu’en vrai ça commence à 0.

— Ils pensent que Index modifie la collection d’origine — mais comme tous les méthodes LINQ, ça renvoie une nouvelle séquence, sans toucher à l’original (approche immuable).

Encore un truc à savoir : si la collection est “paresseuse” (rappel : les requêtes LINQ sont paresseuses par défaut), la méthode Index va aussi calculer les index au fur et à mesure que tu accèdes aux éléments, pas à l’avance. C’est nickel pour bosser avec des séquences grandes ou même infinies — l’indexation sera toujours correcte et ça va pas exploser ta RAM.

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