Quels avantages des méthodes d’extension avez-vous trouvé?

Un “non-croyant” de C # me demandait quel était le but des méthodes d’extension. J’ai expliqué que vous pouviez alors append de nouvelles méthodes aux objects déjà définis, en particulier lorsque vous ne possédez / contrôlez pas la source de l’object d’origine.

Il a soulevé “Pourquoi ne pas simplement append une méthode à votre propre classe?” Nous avons fait le tour du monde (dans le bon sens). Ma réponse générale est que c’est un autre outil dans la boîte à outils, et sa réponse est que c’est un gaspillage inutile d’un outil … mais je pensais avoir une réponse plus “éclairée”.

Quels sont les scénarios que vous avez utilisés avec des méthodes d’extension que vous n’auriez pas pu (ou ne devriez pas avoir) utilisé avec une méthode ajoutée à votre propre classe?

Je pense que les méthodes d’extension aident beaucoup lors de l’écriture de code, si vous ajoutez des méthodes d’extension aux types de base, vous les obtiendrez rapidement dans intellisense.

J’ai un fournisseur de format pour formater une taille de fichier . Pour l’utiliser, j’ai besoin d’écrire:

 Console.WriteLine(Ssortingng.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize)); 

Créer une méthode d’extension que je peux écrire:

 Console.WriteLine(fileSize.ToFileSize()); 

Plus propre et plus simple

Le seul avantage des méthodes d’extension est la lisibilité du code. C’est tout.

Les méthodes d’extension vous permettent de faire ceci:

 foo.bar(); 

au lieu de cela:

 Util.bar(foo); 

Maintenant, il y a beaucoup de choses en C # qui sont comme ça. En d’autres termes, il existe de nombreuses fonctionnalités en C # qui semblent sortingviales et qui ne sont pas très utiles. Cependant, une fois que vous commencez à combiner ces fonctionnalités, vous commencez à voir quelque chose d’un peu plus grand que la sum de ses parties. LINQ bénéficie grandement des méthodes d’extension, car les requêtes LINQ seraient presque illisibles sans elles. LINQ serait possible sans méthodes d’extension, mais pas pratique.

Les méthodes d’extension ressemblent beaucoup aux classes partielles de C #. En eux-mêmes, ils ne sont pas très utiles et semblent sortingviaux. Mais lorsque vous commencez à travailler avec une classe nécessitant du code généré, les classes partielles commencent à prendre beaucoup plus de sens.

N’oubliez pas les outils! Lorsque vous ajoutez une méthode d’extension M sur le type Foo, vous obtenez “M” dans la liste intellisense de Foo (en supposant que la classe d’extension est dans la scope). Cela rend ‘M’ plus facile à trouver que MyClass.M (Foo, …).

Au bout du compte, ce n’est que du sucre syntaxique pour les méthodes statiques, mais comme l’achat d’une maison: «emplacement, emplacement, emplacement! Si cela dépend du type, les gens le trouveront!

Deux autres avantages des méthodes d’extension que j’ai rencontrés:

  • une interface fluide peut être encapsulée dans une classe statique de méthodes d’extension, réalisant ainsi une séparation des préoccupations entre la classe principale et ses extensions courantes; j’ai vu que réaliser une plus grande maintenabilité
  • Les méthodes d’extension peuvent être supprimées des interfaces, vous permettant ainsi de spécifier un contrat (via une interface) et une série associée de comportements basés sur l’interface (via des méthodes d’extension), offrant à nouveau une séparation des préoccupations.

Certaines des meilleures utilisations de mes méthodes d’extension sont la possibilité de:

  1. Étendre les fonctionnalités aux objects tiers (qu’ils soient commerciaux ou internes à mon entreprise, mais gérés par un groupe distinct), qui seront souvent marqués comme sealed .
  2. Créer une fonctionnalité par défaut pour les interfaces sans avoir à implémenter une classe abstraite

Prenons par exemple IEnumerable . Bien qu’il soit riche en méthodes d’extension, j’ai trouvé ennuyeux de ne pas avoir implémenté de méthode ForEach générique. Donc, j’ai fait le mien:

 public void ForEach(this IEnumerable enumerable, Action action) { foreach ( var o in enumerable ) { action(o); } } 

Voila, tous mes objects IEnumerable , quel que soit le type d’implémentation, et si je l’ai écrit ou non, quelqu’un a maintenant une méthode ForEach en ajoutant une instruction “using” appropriée dans mon code.

L’une des principales raisons d’utiliser les méthodes d’extension est LINQ. Sans méthodes d’extension, une grande partie de ce que vous pouvez faire dans LINQ serait très difficile. Les méthodes d’extension Where (), Contains () et Select signifient que beaucoup plus de fonctionnalités sont ajoutées aux types existants sans modifier leur structure.

Les avantages des méthodes d’extension sont nombreux. Que diriez-vous de remédier aux inconvénients ?

Le plus grand inconvénient est qu’il n’y a pas d’erreur ou d’avertissement du compilateur si vous avez une méthode régulière et une méthode d’extension avec la même signature dans le même contexte.

Supposons que vous créez une méthode d’extension s’appliquant à une classe particulière. Ensuite, quelqu’un crée une méthode avec une signature identique sur cette classe elle-même.

Votre code sera compilé et vous ne pourrez peut-être même pas obtenir d’erreur d’exécution. Mais vous n’exécutez plus le même code qu’auparavant.

Interfaces fluides et sensibilité contextuelle démontrées par Greg Young sur CodeBetter

Mon argument personnel pour les méthodes d’extension est qu’elles s’intègrent très bien dans une conception OOP: considérez la méthode simple

 bool empty = Ssortingng.IsNullOrEmpty (mySsortingng) 

en comparaison à

 bool empty = mySsortingng.IsNullOrEmpty (); 

Il y a des tas de bonnes réponses sur ce que les méthodes d’extension vous permettent de faire.

Ma réponse courte est – ils éliminent presque le besoin d’usines.

Je ferai juste remarquer qu’ils ne sont pas un nouveau concept et que l’une des plus importantes validations est qu’ils sont une fonctionnalité géniale dans Objective-C ( catégories ). Ils ajoutent tellement de flexibilité au développement basé sur le framework que NeXT avait comme principaux utilisateurs les modélisateurs financiers NSA et Wall Street.

REALbasic les implémente également en tant que méthodes étendues et elles ont été utilisées de la même manière pour simplifier le développement.

Je voudrais soutenir les autres réponses ici qui mentionnent l’amélioration de la lisibilité du code comme une raison importante derrière les méthodes d’extension. Je vais le démontrer avec deux aspects: le chaînage de méthodes par rapport aux appels de méthodes nestedes, et l’encombrement d’une requête LINQ avec des noms de classes statiques sans signification.


Prenons cette requête LINQ comme exemple:

 numbers.Where(x => x > 0).Select(x => -x) 

Where et Select sont des méthodes d’extension, définies dans la classe statique Enumerable . Ainsi, si les méthodes d’extension n’existaient pas, et qu’il s’agissait de méthodes statiques normales, la dernière ligne de code devrait essentiellement ressembler à ceci:

 Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x) 

Voyez combien cette requête vient d’être


Deuxièmement, si vous vouliez maintenant introduire votre propre opérateur de requête, vous n’auriez naturellement aucun moyen de le définir dans la classe statique Enumerable , comme tous les autres opérateurs de requête standard, car Enumerable est dans la structure et vous n’avez aucun contrôle sur cette classe. . Par conséquent, vous devez définir votre propre classe statique contenant des méthodes d’extension. Vous pourriez alors obtenir des requêtes telles que celle-ci:

 Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x) // ^^^^^^^^^^^^^^^^^^^^^^ // different class name that has zero informational value // and, as with 'Enumerable.xxxxxx', only obstructs the // query's actual meaning. 

C’est vrai que vous pouvez append votre méthode (d’extension) directement dans votre classe. Mais toutes les classes ne sont pas écrites par vous. Les classes de la bibliothèque principale ou des bibliothèques tierces sont souvent fermées et il serait impossible d’obtenir le sucre syntatique sans méthodes d’extension. Mais rappelez-vous que les méthodes d’extension sont comme les méthodes autonomes (statiques) par exemple. c ++

Les méthodes d’extension peuvent également aider à garder vos classes et dépendances de classe propres. Par exemple, vous pouvez avoir besoin d’une méthode Bar () pour la classe Foo partout où Foo est utilisé. Toutefois, vous pouvez souhaiter une méthode .ToXml () dans un autre assembly et uniquement pour cet assembly. Dans ce cas, vous pouvez append les dépendances System.Xml et / ou System.Xml.Linq nécessaires à cet assembly et non à l’assembly d’origine.

Avantages: les dépendances dans votre assembly de classe de définition sont réduites au ssortingct nécessaire et les autres assemblages consommateurs ne pourront pas utiliser la méthode ToXml (). Voir cette présentation PDC pour plus de références.

Les méthodes d’extension sont en réalité l’incorporation .NET du refactor “Introduce Foreign Method” du Book de Martin Fowler (jusqu’à la signature de la méthode). Ils viennent fondamentalement avec les mêmes avantages et les mêmes pièges. Dans la section sur ce refactor, il dit qu’il s’agit d’une solution de rechange lorsque vous ne pouvez pas modifier la classe qui doit réellement posséder la méthode.

Je considère principalement les méthodes d’extension comme une reconnaissance du fait qu’elles ne devraient peut-être pas interdire les fonctions libres.

Dans la communauté C ++, il est souvent considéré comme une bonne pratique OOP de préférer les fonctions non membres libres aux membres, car ces fonctions ne rompent pas l’encapsulation en accédant aux membres privés dont elles n’ont pas besoin. Les méthodes d’extension semblent être un moyen détourné pour parvenir à la même chose. C’est-à-dire une syntaxe plus propre pour les fonctions statiques qui n’ont pas access aux membres privés.

Les méthodes d’extension ne sont rien de plus que du sucre syntaxique, mais je ne vois aucun mal à les utiliser.

  • Intellisense sur l’object lui-même au lieu d’appeler une fonction d’utilitaire laide
  • Pour les fonctions de conversion, vous pouvez remplacer “XToY (X x)” par “ToY (this X x)”, ce qui donne un joli x.ToY () au lieu de XToY (x).
  • Étendre les classes sur lesquelles vous n’avez aucun contrôle
  • Étendre les fonctionnalités des classes lorsqu’il n’est pas souhaitable d’append des méthodes aux classes elles-mêmes. Par exemple, vous pouvez garder les objects métier simples et sans logique, et append une logique métier spécifique avec des dépendances désagréables dans les méthodes d’extension

Je les utilise pour réutiliser mes classes de modèle d’object. J’ai un tas de classes qui représentent des objects que j’ai dans une firebase database. Ces classes sont utilisées du côté client uniquement pour afficher les objects afin que l’utilisation de base accède aux propriétés.

 public class Stock { public Code { get; private set; } public Name { get; private set; } } 

En raison de ce modèle d’utilisation, je ne souhaite pas avoir de méthodes logiques dans ces classes. Je fais donc de chaque logique métier une méthode d’extension.

 public static class StockExtender { public static List  GetQuotesByDate(this Stock s, DateTime date) {...} } 

De cette façon, je peux utiliser les mêmes classes pour le traitement de la logique métier et pour l’affichage de l’interface utilisateur sans surcharger le client avec du code inutile.

Une chose intéressante à propos de cette solution est que mes classes de modèles d’objects sont générées de manière dynamic à l’aide de Mono.Cecil , il serait donc très difficile d’append des méthodes de logique métier même si je le voulais. J’ai un compilateur qui lit les fichiers de définition XML et génère ces classes de stubs représentant un object que j’ai dans la firebase database. La seule approche dans ce cas est de les étendre.

Je suis d’accord que les méthodes d’extension augmentent la lisibilité du code, mais ce n’est vraiment rien d’autre que des méthodes d’assistance statiques.

IMO utilisant des méthodes d’extension pour append un comportement à vos classes peut être:

Confusion: les programmeurs peuvent croire que les méthodes font partie du type étendu, ne comprenant donc pas pourquoi les méthodes ont disparu lorsque l’espace de noms d’extension n’est pas importé.

Un antipattern: Vous décidez d’append un comportement aux types dans votre structure en utilisant des méthodes d’extension, puis de les envoyer à une personne qui effectue des tests unitaires. Maintenant, il est coincé avec un cadre contenant un tas de méthodes qu’il ne peut pas tromper.

Il permet à C # de mieux supporter les langages dynamics, LINQ et une douzaine d’autres choses. Consultez l’article de Scott Guthrie .

Dans mon dernier projet, j’ai utilisé une méthode d’extension pour associer des méthodes Validate () à des objects métier. Je justifiais cela parce que les objects métier étaient des objects de transfert de données sérialisables et seraient utilisés dans des domaines différents, car ils étaient des entités de commerce électronique telles que produit, client, commerçant, etc. logique de validation liée tardivement dans une méthode Validate associée à la classe de base de mes objects de transfert de données. J’espère que cela a du sens 🙂

Un cas où les méthodes d’extension étaient très utiles concernait une application client utilisant les services Web ASMX. En raison de la sérialisation, les types de retour des méthodes Web ne contiennent aucune méthode (seules les propriétés publiques de ces types sont disponibles sur le client).

Méthodes d’extension autorisées pour append des fonctionnalités (côté client) aux types renvoyés par les méthodes Web sans avoir à créer un autre modèle d’object ou de nombreuses classes wrapper côté client.

Rappelez-vous également que les méthodes d’extension ont été ajoutées pour aider la requête Linq à être plus lisible, lorsqu’elle est utilisée dans son style C #.

Ces deux affectations sont tout à fait équivalentes, mais la première est beaucoup plus lisible (et le manque de lisibilité augmenterait bien sûr avec plus de méthodes enchaînées).

 int n1 = new List {1,2,3}.Where(i => i % 2 != 0).Last(); int n2 = Enumerable.Last(Enumerable.Where(new List {1,2,3}, i => i % 2 != 0)); 

Notez que la syntaxe complète doit même être:

 int n1 = new List {1,2,3}.Where(i => i % 2 != 0).Last(); int n2 = Enumerable.Last(Enumerable.Where(new List {1,2,3}, i => i % 2 != 0)); 

Par chance, les parameters de type Where et Last n’ont pas besoin d’être mentionnés explicitement car ils peuvent être déduits grâce à la présence du premier paramètre de ces deux méthodes (le paramètre introduit par le mot this clé this et leur atsortingbuant des méthodes d’extension). ).

Ce point est évidemment un avantage (parmi d’autres) des méthodes d’extension, et vous pouvez en tirer profit dans tous les scénarios similaires impliquant le chaînage des méthodes.

En particulier, c’est la manière la plus élégante et convaincante de trouver une méthode de classe de base invocable par une sous-classe et de renvoyer une référence fortement typée à cette sous-classe (avec le type de sous-classe).

Exemple (bon, ce scénario est totalement ringard): après une bonne nuit, un animal ouvre les yeux puis crie; chaque animal ouvre les yeux de la même façon, alors qu’un chien aboie et un canard kwaks.

 public abstract class Animal { //some code common to all animals } public static class AnimalExtension { public static TAnimal OpenTheEyes(this TAnimal animal) where TAnimal : Animal { //Some code to flutter one's eyelashes and then open wide return animal; //returning a self reference to allow method chaining } } public class Dog : Animal { public void Bark() { /* ... */ } } public class Duck : Animal { public void Kwak() { /* ... */ } } class Program { static void Main(ssortingng[] args) { Dog Goofy = new Dog(); Duck Donald = new Duck(); Goofy.OpenTheEyes().Bark(); //*1 Donald.OpenTheEyes().Kwak(); //*2 } } 

Conceptuellement, OpenTheEyes devrait être une méthode Animal , mais elle renverrait alors une instance de la classe abstraite Animal , qui ne connaît pas de méthodes spécifiques à la sous-classe comme Bark ou Duck ou autre. Les 2 lignes commentées comme * 1 et * 2 déclencheraient alors une erreur de compilation.

Mais grâce aux méthodes d’extension, on peut avoir une sorte de “méthode de base qui connaît le type de sous-classe sur lequel elle s’appelle”.

Notez qu’une méthode générique simple aurait pu faire l’affaire, mais de manière beaucoup plus délicate:

 public abstract class Animal { //some code common to all animals public TAnimal OpenTheEyes() where TAnimal : Animal { //Some code to flutter one's eyelashes and then open wide return (TAnimal)this; //returning a self reference to allow method chaining } } 

Cette fois, pas de paramètre et donc pas d’inférence de type de retour possible. L’appel ne peut être autre que:

 Goofy.OpenTheEyes().Bark(); Donald.OpenTheEyes().Kwak(); 

… ce qui peut peser beaucoup le code si plus de chaînage est impliqué (surtout en sachant que le paramètre type sera toujours sur la ligne de Goofy et sur celui de Donald …)

Je n’ai qu’un mot à dire à ce sujet: MAINTIEN DE LA DURABILITE c’est la clé de l’utilisation des méthodes d’extension

Je pense que les méthodes d’extension permettent d’écrire du code plus clair.

Au lieu de mettre une nouvelle méthode dans votre classe, comme l’a suggéré votre ami, vous la placez dans l’espace de noms ExtensionMethods. De cette façon, vous maintenez un sens logique de l’ordre dans votre classe. Les méthodes qui ne traitent pas directement avec votre classe ne seront pas encombrantes.

Je pense que les méthodes d’extension rendent votre code plus clair et mieux organisé.

Il permet à votre éditeur / IDE de faire automatiquement une suggestion complète.

Je les aime pour la construction de HTML. Fréquemment, il y a des sections qui sont utilisées de manière répétée ou générées de manière récursive lorsqu’une fonction est utile mais pourrait autrement interrompre le déroulement du programme.

  HTML_Out.Append("
    "); foreach (var i in items) if (i.Description != "") { HTML_Out.Append("
  • ") .AppendAnchor(new ssortingng[]{ urlRoot, i.Description_Norm }, i.Description) .Append("
    ") .AppendImage(iconDir, i.Icon, i.Description) .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("
  • "); } return HTML_Out.Append("
").ToSsortingng();

Il existe également des situations où un object nécessite une logique personnalisée pour être préparé pour une sortie HTML. Les méthodes d’extension vous permettent d’append cette fonctionnalité sans mélanger la présentation et la logique dans la classe.

J’ai trouvé que les méthodes d’extension sont utiles pour faire correspondre les arguments génériques nesteds.

Cela semble un peu étrange – mais disons que nous avons une classe générique MyGenericClass , et nous soaps que TList lui-même est générique (par exemple une List ), je ne pense pas qu’il y ait un moyen de trouver T ‘de la liste sans méthode d’extension ni méthode d’assistance statique. Si nous ne disposons que de méthodes d’assistance statiques, c’est (a) moche, et (b) nous obligera à déplacer les fonctionnalités appartenant à la classe vers un emplacement externe.

Par exemple, pour récupérer les types dans un tuple et les convertir en signature de méthode, nous pouvons utiliser des méthodes d’extension:

 public class Tuple { } public class Tuple : Tuple { } public class Tuple : Tuple { } public class Caller where TTuple : Tuple { /* ... */ } public static class CallerExtensions { public static void Call(this Caller> caller, T0 p0) { /* ... */ } public static void Call(this Caller> caller, T0 p0, T1 p1) { /* ... */ } } new Caller>().Call(10); new Caller>().Call("Hello", 10); 

Cela dit, je ne suis pas sûr de la ligne de démarcation – quand une méthode devrait-elle être une méthode d’extension, et quand devrait-elle être une méthode d’assistance statique? Des pensées?

Les méthodes d’extension peuvent être utilisées pour créer une sorte de mixin en C #.

Ceci, à son tour, offre une meilleure séparation des préoccupations pour les concepts orthogonaux. Jetez un oeil à cette réponse comme exemple.

Cela peut également être utilisé pour activer des rôles dans C #, un concept central dans l’ architecture DCI .

J’ai des zones de saisie sur mon écran et toutes doivent implémenter un comportement standard quels que soient leurs types exacts (zones de texte, cases à cocher, etc.). Ils ne peuvent pas hériter d’une classe de base commune car chaque type de zone d’entrée dérive déjà d’une classe spécifique (TextInputBox, etc.)

Peut-être qu’en remontant dans la hiérarchie d’inheritance, je pourrais trouver un ancêtre commun, comme WebControl, mais je n’ai pas développé la classe d’infrastructure WebControl et elle n’expose pas ce dont j’ai besoin.

Avec la méthode d’extension, je peux:

1) étendre la classe WebControl, puis obtenir mon comportement standard unifié sur toutes mes classes d’entrée

2) alternativement, faire en sorte que toutes mes classes dérivent d’une interface, disons IInputZone, et étendre cette interface avec des méthodes. Je vais maintenant pouvoir appeler des méthodes d’extensions liées à l’interface sur toutes mes zones d’entrée. J’ai donc réalisé une sorte d’inheritance multiple puisque mes zones d’entrée provenaient déjà de plusieurs classes de base.

Il y a tellement d’excellents exemples de méthodes d’extension. Surtout sur IEnumerables comme indiqué ci-dessus.

Par exemple, si j’ai un IEnumerable je peux créer et étendre la méthode pour IEnumerable

 mylist List; 

… créer la liste

 mylist.DisplayInMyWay(); 

Sans les méthodes d’extension, il faudrait appeler:

 myDisplayMethod(myOldArray); // can create more nexted brackets. 

Un autre bon exemple est la création d’une liste liée circulaire en un clin d’œil!

Je peux en prendre le crédit!

liste chaînée circulaire utilisant des méthodes d’extension

Maintenant, combinez-les et en utilisant des méthodes d’extension, le code se lit comme suit.

 myNode.NextOrFirst().DisplayInMyWay(); 

plutôt que

 DisplayInMyWay(NextOrFirst(myNode)). 

utiliser les méthodes d’extension C’est plus simple et plus facile à lire et plus orienté object. également très proche de:

 myNode.Next.DoSomething() 

Montrez cela à votre collègue! 🙂