Puis-je append des méthodes d’extension à une classe statique existante?

Je suis fan des méthodes d’extension en C #, mais je n’ai pas réussi à append une méthode d’extension à une classe statique, telle que Console.

Par exemple, si je veux append une extension à Console, appelée «WriteBlueLine», pour que je puisse y aller:

Console.WriteBlueLine("This text is blue"); 

J’ai essayé ceci en ajoutant une méthode locale, statique et publique, avec Console comme paramètre “this” … mais pas de dés!

 public static class Helpers { public static void WriteBlueLine(this Console c, ssortingng text) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(text); Console.ResetColor(); } } 

Cela n’a pas ajouté de méthode “WriteBlueLine” à la console … Est-ce que je le fais mal? Ou demander l’impossible?

Les méthodes d’extension nécessitent une variable d’instance (valeur) pour un object. Vous pouvez cependant écrire un wrapper statique autour de l’interface ConfigurationManager . Si vous implémentez le wrapper, vous n’avez pas besoin d’une méthode d’extension car vous pouvez simplement append la méthode directement.

  public static class ConfigurationManagerWrapper { public static ConfigurationSection GetSection( ssortingng name ) { return ConfigurationManager.GetSection( name ); } ..... public static ConfigurationSection GetWidgetSection() { return GetSection( "widgets" ); } } 

Pouvez-vous append des extensions statiques aux classes en C #? Non mais vous pouvez le faire:

 public static class Extensions { public static T Create(this T @this) where T : class, new() { return Utility.Create(); } } public static class Utility where T : class, new() { static Utility() { Create = Expression.Lambda>(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Comstack(); } public static Func Create { get; private set; } } 

Voici comment cela fonctionne. Bien que vous ne puissiez pas techniquement écrire des méthodes d’extension statique, ce code exploite plutôt une faille dans les méthodes d’extension. Cette lacune est que vous pouvez appeler des méthodes d’extension sur des objects nuls sans obtenir l’exception nulle (sauf si vous accédez à quelque chose via @this).

Alors, voici comment vous utiliseriez ceci:

  var ds1 = (null as DataSet).Create(); // as oppose to DataSet.Create() // or DataSet ds2 = null; ds2 = ds2.Create(); // using some of the techniques above you could have this: (null as Console).WriteBlueLine(...); // as oppose to Console.WriteBlueLine(...) 

Maintenant POURQUOI ai-je choisi d’appeler le constructeur par défaut comme exemple, ET ET pourquoi est-ce que je ne renvoie pas simplement T () dans le premier extrait de code sans faire toute cette erreur Expression? Eh bien aujourd’hui votre jour de chance parce que vous obtenez un 2fer. Comme tout développeur .NET avancé le sait, new T () est lent car il génère un appel à System.Activator qui utilise la reflection pour obtenir le constructeur par défaut avant de l’appeler. Damn you Microsoft! Cependant, mon code appelle directement le constructeur par défaut de l’object.

Les extensions statiques seraient meilleures que cela, mais les temps désespérés exigent des mesures désespérées.

Ce n’est pas possible.

Et oui, je pense que MS a fait une erreur ici.

Leur décision n’a pas de sens et oblige les programmeurs à écrire (comme décrit ci-dessus) une classe wrapper inutile.

Voici un bon exemple: Essayer d’étendre la classe de test statique MS Unit Assert: Je veux 1 méthode AreEqual(x1,x2) plus AreEqual(x1,x2) .

La seule manière de procéder consiste à pointer vers différentes classes ou à écrire un wrapper autour de 100 méthodes Assert différentes. Pourquoi!?

Si la décision a été prise d’autoriser les extensions d’instances, je ne vois aucune raison logique pour ne pas autoriser les extensions statiques. Les arguments relatifs à la section des bibliothèques ne sont pas résolus une fois que les instances peuvent être étendues.

Vous pourriez peut-être append une classe statique avec votre espace de noms personnalisé et le même nom de classe:

 using CLRConsole = System.Console; namespace ExtensionMethodsDemo { public static class Console { public static void WriteLine(ssortingng value) { CLRConsole.WriteLine(value); } public static void WriteBlueLine(ssortingng value) { System.ConsoleColor currentColor = CLRConsole.ForegroundColor; CLRConsole.ForegroundColor = System.ConsoleColor.Blue; CLRConsole.WriteLine(value); CLRConsole.ForegroundColor = currentColor; } public static System.ConsoleKeyInfo ReadKey(bool intercept) { return CLRConsole.ReadKey(intercept); } } class Program { static void Main(ssortingng[] args) { try { Console.WriteBlueLine("This text is blue"); } catch (System.Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); } Console.WriteLine("Press any key to continue..."); Console.ReadKey(true); } } } 

J’ai trébuché sur ce fil tout en essayant de trouver une réponse à la même question que le PO. Je n’ai pas trouvé la réponse que je voulais mais j’ai fini par le faire.

 public static class MyConsole { public static void WriteLine(this ConsoleColor Color, ssortingng Text) { Console.ForegroundColor = Color; Console.WriteLine(Text); } } 

Et je l’utilise comme ceci:

 ConsoleColor.Cyan.WriteLine("voilà"); 

Nan. Les définitions de méthode d’extension nécessitent une instance du type que vous étendez. C’est regrettable; Je ne sais pas pourquoi c’est nécessaire …

En ce qui concerne les méthodes d’extension, les méthodes d’extension elles-mêmes sont statiques; mais ils sont invoqués comme s’ils étaient des méthodes d’instance. Étant donné qu’une classe statique n’est pas instanciable, vous n’aurez jamais d’instance de la classe à laquelle invoquer une méthode d’extension. Pour cette raison, le compilateur ne permet pas de définir les méthodes d’extension pour les classes statiques.

M. Obnoxious a écrit: “Comme tout développeur .NET avancé le sait, new T () est lent car il génère un appel à System.Activator qui utilise la reflection pour obtenir le constructeur par défaut avant de l’appeler”.

New () est compilé dans l’instruction IL “newobj” si le type est connu à la compilation. Newobj prend un constructeur pour l’invocation directe. Les appels à System.Activator.CreateInstance () comstacknt à l’instruction IL “call” pour appeler System.Activator.CreateInstance (). New (), lorsqu’il est utilisé avec des types génériques, provoquera un appel à System.Activator.CreateInstance (). Le message de M. Obnoxious n’était pas clair sur ce point … et bien, odieux.

Ce code:

 System.Collections.ArrayList _al = new System.Collections.ArrayList(); System.Collections.ArrayList _al2 = (System.Collections.ArrayList)System.Activator.CreateInstance(typeof(System.Collections.ArrayList)); 

produit cette IL:

  .locals init ([0] class [mscorlib]System.Collections.ArrayList _al, [1] class [mscorlib]System.Collections.ArrayList _al2) IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() IL_0006: stloc.0 IL_0007: ldtoken [mscorlib]System.Collections.ArrayList IL_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0011: call object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type) IL_0016: castclass [mscorlib]System.Collections.ArrayList IL_001b: stloc.1 

Vous ne pouvez pas append de méthodes statiques à un type. Vous ne pouvez append que des méthodes d’instance (pseudo) à une instance d’un type.

Le but de this modificateur est de dire au compilateur C # de passer l’instance sur le côté gauche du fichier . comme premier paramètre de la méthode statique / d’extension.

Dans le cas de l’ajout de méthodes statiques à un type, il n’y a pas d’instance à transmettre pour le premier paramètre.

A partir de C # 7, ce n’est pas supporté. Il y a cependant des discussions sur l’intégration de quelque chose comme ça dans C # 8 et des propositions qui méritent d’être soutenues .

J’ai essayé de le faire avec System.Environment lorsque j’apprenais des méthodes d’extension et que cela n’a pas réussi. La raison en est, comme d’autres le mentionnent, car les méthodes d’extension nécessitent une instance de la classe.

oui, dans un sens limité.

 public class DataSet : System.Data.DataSet { public static void SpecialMethod() { } } 

Cela fonctionne mais la console ne fonctionne pas parce que c’est statique.

 public static class Console { public static void WriteLine(Ssortingng x) { System.Console.WriteLine(x); } public static void WriteBlueLine(Ssortingng x) { System.Console.ForegroundColor = ConsoleColor.Blue; System.Console.Write(.x); } } 

Cela fonctionne parce que tant que ce n’est pas sur le même espace de noms. Le problème est que vous devez écrire une méthode proxy statique pour chaque méthode de System.Console. Ce n’est pas nécessairement une mauvaise chose car vous pouvez append quelque chose comme ceci:

  public static void WriteLine(Ssortingng x) { System.Console.WriteLine(x.Replace("Fck","****")); } 

ou

  public static void WriteLine(Ssortingng x) { System.Console.ForegroundColor = ConsoleColor.Blue; System.Console.WriteLine(x); } 

La façon dont cela fonctionne est que vous connectez quelque chose à la WriteLine standard. Il pourrait s’agir d’un compte de lignes ou d’un mauvais filtre de mots ou autre. Lorsque vous spécifiez simplement Console dans votre espace de noms, par exemple WebProject1 et importez l’espace de noms System, WebProject1.Console sera choisi par défaut sur System.Console pour les classes de l’espace de noms WebProject1. Donc, ce code transformera tous les appels Console.WriteLine en bleu dans la mesure où vous n’avez jamais spécifié System.Console.WriteLine.

Ce qui suit a été rejeté en tant que modification de la réponse de tvanfosson. On m’a demandé de consortingbuer comme ma propre réponse. J’ai utilisé sa suggestion et terminé l’implémentation d’un wrapper ConfigurationManager . En principe, j’ai simplement rempli le ... dans la réponse de tvanfosson.

Les méthodes d’extension nécessitent une instance d’un object. Vous pouvez cependant écrire un wrapper statique autour de l’interface ConfigurationManager. Si vous implémentez le wrapper, vous n’avez pas besoin d’une méthode d’extension car vous pouvez simplement append la méthode directement.

 public static class ConfigurationManagerWrapper { public static NameValueCollection AppSettings { get { return ConfigurationManager.AppSettings; } } public static ConnectionSsortingngSettingsCollection ConnectionSsortingngs { get { return ConfigurationManager.ConnectionSsortingngs; } } public static object GetSection(ssortingng sectionName) { return ConfigurationManager.GetSection(sectionName); } public static Configuration OpenExeConfiguration(ssortingng exePath) { return ConfigurationManager.OpenExeConfiguration(exePath); } public static Configuration OpenMachineConfiguration() { return ConfigurationManager.OpenMachineConfiguration(); } public static Configuration OpenMappedExeConfiguration(ExeConfigurationFileMap fileMap, ConfigurationUserLevel userLevel) { return ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel); } public static Configuration OpenMappedMachineConfiguration(ConfigurationFileMap fileMap) { return ConfigurationManager.OpenMappedMachineConfiguration(fileMap); } public static void RefreshSection(ssortingng sectionName) { ConfigurationManager.RefreshSection(sectionName); } } 

Vous pouvez utiliser un cast sur null pour le faire fonctionner.

 public static class YoutTypeExtensionExample { public static void Example() { ((YourType)null).ExtensionMethod(); } } 

L’extension:

 public static class YourTypeExtension { public static void ExtensionMethod(this YourType x) { } } 

Ton type:

 public class YourType { } 

Il n’est pas possible d’écrire une méthode d’extension, mais il est possible d’imiter le comportement que vous demandez.

 using FooConsole = System.Console; public static class Console { public static void WriteBlueLine(ssortingng text) { FooConsole.ForegroundColor = ConsoleColor.Blue; FooConsole.WriteLine(text); FooConsole.ResetColor(); } } 

Cela vous permettra d’appeler Console.WriteBlueLine (fooText) dans d’autres classes. Si les autres classes veulent accéder aux autres fonctions statiques de la console, elles devront être explicitement référencées dans leur espace de noms.

Vous pouvez toujours append toutes les méthodes à la classe de remplacement si vous souhaitez qu’elles soient toutes au même endroit.

Donc, vous auriez quelque chose comme

 using FooConsole = System.Console; public static class Console { public static void WriteBlueLine(ssortingng text) { FooConsole.ForegroundColor = ConsoleColor.Blue; FooConsole.WriteLine(text); FooConsole.ResetColor(); } public static void WriteLine(ssortingng text) { FooConsole.WriteLine(text); } ...etc. } 

Cela fournirait le genre de comportement que vous recherchez.

* La console de notes devra être ajoutée dans l’espace de noms que vous avez inséré.

Vous POUVEZ faire ceci si vous êtes prêt à le “frig” un peu en créant une variable de la classe statique et en lui assignant null. Cependant, cette méthode ne serait pas disponible pour les appels statiques sur la classe, donc vous ne savez pas quelle serait l’utilisation:

 Console myConsole = null; myConsole.WriteBlueLine("my blue line"); public static class Helpers { public static void WriteBlueLine(this Console c, ssortingng text) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(text); Console.ResetColor(); } }