Propriétés d'extension en C# : quand on veut mais on ne peut pas (et comment contourner)
Imaginez la situation : vous travaillez avec une bibliothèque où la classe Person a une propriété FullName. Tout va bien, mais voilà le problème : vous devez ajouter une propriété calculée AgeInDays qui calcule l'âge en jours à partir de la date de naissance. Vous êtes habitué aux méthodes d'extension et vous pensez : "Et pourquoi ne pas créer des propriétés d'extension ?"
Et là, vous allez sur Stack Overflow, vous voyez une question avec des milliers de vues : "Does C# have extension properties?" — et vous lisez la réponse avec déception : "Non, elles ne sont pas officiellement prises en charge". Mais ne fermez pas l'onglet ! Dans cet article, nous allons non seulement comprendre pourquoi elles n'existent pas, mais aussi trouver des solutions élégantes qui ne sacrifient pas les performances.
Pourquoi C# n'ajoute-t-il pas les propriétés d'extension ? Contexte historique
Commençons par l'essentiel : les propriétés d'extension ne sont pas un caprice, mais une demande de longue date de la communauté. Dès 2016, une proposition est apparue sur GitHub (demande de fonctionnalité C#), mais elle n'est toujours pas implémentée. Pourquoi ?
- Complexité avec le compilateur : Contrairement aux méthodes, les propriétés ont des getters et des setters qui doivent être liés à une instance spécifique. Étendre cette logique au niveau syntaxique nécessite des modifications importantes du compilateur.
- Conflit avec les indexeurs : Les propriétés et les indexeurs en C# sont étroitement liés. L'ajout de propriétés d'extension pourrait créer des ambiguïtés.
- Priorité d'autres fonctionnalités : L'équipe C# s'est concentrée sur les records, le pattern matching et les membres abstraits statiques (C# 11), qui apportent plus de valeur pour la plupart des scénarios.
Mais l'absence de support officiel n'est pas une condamnation. Voyons comment les développeurs expérimentés résolvent ce problème.
Méthodes d'extension : votre outil principal (mais avec des nuances)
La méthode la plus évidente est d'utiliser des méthodes d'extension au lieu de propriétés. Au lieu de person.AgeInDays, vous écrivez person.GetAgeInDays(). Cela fonctionne, mais viole le principe d'uniformité : dans le reste du code, vous utilisez des propriétés, et là soudainement une méthode.
public static class PersonExtensions{ public static int GetAgeInDays(this Person person) { return (DateTime.Now - person.BirthDate).Days; }}
// Utilisation :var age = person.GetAgeInDays(); // Fonctionne, mais pas élégantInconvénient : le code devient moins lisible, surtout dans les chaînes d'appels LINQ. Avantage : c'est totalement gratuit en termes de performances — l'appel d'une méthode d'extension ne diffère en rien d'une méthode statique ordinaire.
Alternative n°1 : Fabriques statiques et classes auxiliaires
Si vous avez besoin d'une "propriété" calculée à partir de données, vous pouvez créer une classe statique d'aide :
public static class PersonHelper{ public static int AgeInDays(Person person) => (DateTime.Now - person.BirthDate).Days;}
// Utilisation :var age = PersonHelper.AgeInDays(person);C'est plus honnête qu'une méthode d'extension : on voit immédiatement qu'il s'agit d'une fonction auxiliaire. Mais ce n'est toujours pas une propriété.
Alternative n°2 : Attributs et génération de code (Source Generators)
Pour les scénarios complexes (par exemple, ajouter des propriétés à des classes provenant de paquets NuGet), vous pouvez utiliser des générateurs de source. Ils sont apparus dans .NET 5 et permettent de générer du code à la compilation. Vous écrivez un attribut, et le générateur crée une classe partielle avec les propriétés souhaitées.
[AutoExtensionProperty(typeof(Person), nameof(Person.BirthDate))]public partial class PersonExtensions{ // Le générateur créera la propriété AgeInDays}
// Code généré :public partial class PersonExt