Introduction : pourquoi la performance est importante dès les premières lignes de code
Chaque développeur, même débutant, rencontre des situations où une application simple commence à ralentir, consommer trop de mémoire ou traiter les requêtes lentement. L'optimisation des performances des applications C# n'est pas de la magie ni réservée aux développeurs seniors. C'est un ensemble de règles pratiques et d'outils que l'on peut maîtriser à n'importe quelle étape de l'apprentissage.
Dans cet article, nous aborderons les aspects clés de l'optimisation du code en C# (.NET). Vous apprendrez à éviter les erreurs typiques des débutants, à accélérer le travail avec la mémoire, les chaînes de caractères et les collections, ainsi qu'à utiliser correctement le multithreading. Ce contenu est destiné aux développeurs qui connaissent déjà les bases de la syntaxe C# et souhaitent écrire un code plus efficace.
1. Travailler avec les chaînes : pourquoi String est mauvais et StringBuilder est bon
L'un des problèmes de performance les plus courants en C# est une mauvaise gestion des chaînes de caractères. Les chaînes dans .NET sont immuables (immutable). Cela signifie que toute opération de concaténation crée un nouvel objet chaîne en mémoire, tandis que l'ancien objet reste à la merci du ramasse-miettes.
Problème : concaténation dans une boucle
Considérons un mauvais exemple :
string result = "";for (int i = 0; i < 1000; i++){ result += i.ToString() + " ";}// Ici, 1000 chaînes intermédiaires sont créées !Chaque itération de la boucle crée une nouvelle chaîne. Pour 1000 itérations, cela représente 1000 allocations mémoire. Si les itérations sont 100 000, l'application peut "planter" ou provoquer une OutOfMemoryException.
Solution : utiliser StringBuilder
La classe StringBuilder fonctionne avec un tampon de caractères modifiable. Elle ne crée pas de nouveaux objets à chaque ajout de texte.
using System.Text;
StringBuilder sb = new StringBuilder();for (int i = 0; i < 1000; i++){ sb.Append(i); sb.Append(" ");}string result = sb.ToString();Conseil : Utilisez toujours StringBuilder si vous effectuez plus de 3 à 5 opérations de concaténation, surtout dans des boucles. Pour une simple combinaison de quelques chaînes (par exemple, string s = a + b + c;), le compilateur C# optimise cela en un seul appel à string.Concat, donc StringBuilder n'est pas nécessaire ici.
2. Optimisation de LINQ : choisir les bonnes méthodes
LINQ (Language Integrated Query) est un outil puissant, mais une mauvaise utilisation peut fortement ralentir l'application. Le problème principal est l'énumération multiple des collections.
Exemple d'une requête LINQ "coûteuse"
var numbers = Enumerable.Range(1, 1000000);var countEven = numbers.Where(n => n % 2 == 0).Count();// Ici, Where() crée un itérateur, Count() le parcourt.// Tout va bien si la collection est parcourue une seule fois.L'erreur survient lorsque vous appelez plusieurs opérations terminales (Count, ToList, First, Any) sur la même expression LINQ :
var query = numbers.Where(n => n % 2 == 0);int count1 = query.Count(); // Premier passageint count2 = query.Count(); // Deuxième passage !// La collection est parcourue deux fois.Chaque appel à Count() ou ToList() réexécute le filtrage. Si vous avez un filtrage complexe ou une source de données comme une base de données (Entity Framework), cela entraîne des requêtes doubles.
Bonne approche : matérialisation
Si vous devez utiliser le résultat d'une requête plusieurs fois, enregistrez-le dans une liste ou un tableau :
var query = numbers.Where(n => n % 2 == 0).ToList(); // Un seul passageint count1 = query.Count;int count2 = query.Count;Conseil : Utilisez .ToList() ou .ToArray() immédiatement après le filtrage si vous prévoyez d'appeler plusieurs méthodes (Count, First, Sum). Pour un seul appel, laissez