Question Puis-je ajouter 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 à ajouter une méthode d'extension à une classe statique, telle que Console.

Par exemple, si je veux ajouter 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 statique locale et publique, avec la console comme paramètre 'this' ... mais pas de dés!

public static class Helpers {
    public static void WriteBlueLine(this Console c, string 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?


453
2017-10-30 03:54


origine


Réponses:


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

 public static class ConfigurationManagerWrapper
 {
      public static ConfigurationSection GetSection( string name )
      {
         return ConfigurationManager.GetSection( name );
      }

      .....

      public static ConfigurationSection GetWidgetSection()
      {
          return GetSection( "widgets" );
      }
 }

241
2017-11-21 16:44



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

public static class Extensions
{
    public static T Create<T>(this T @this)
        where T : class, new()
    {
        return Utility<T>.Create();
    }
}

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

Voici comment cela fonctionne. Bien que vous ne puissiez pas techniquement écrire des méthodes d'extension statiques, 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 objets 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 ne pas simplement renvoyer un nouveau 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 réflexion pour obtenir le constructeur par défaut avant de l'appeler. Merde vous Microsoft! Cependant, mon code appelle directement le constructeur par défaut de l'objet.

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


82
2018-03-27 19:20



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 MS Unit statique Assert: Je veux 1 méthode Assert plus AreEqual(x1,x2).

La seule façon de le faire est de pointer vers différentes classes ou d'écrire un wrapper autour de 100s de différentes méthodes Assert. 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.


37
2018-01-12 14:44



Vous pourriez peut-être ajouter 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(string value)
        {
            CLRConsole.WriteLine(value);
        }

        public static void WriteBlueLine(string 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(string[] 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);
        }
    }
}

15
2018-01-07 04:11



Nan. Les définitions de méthode d'extension nécessitent une instance du type que vous étendez. C'est regrettable; Je ne suis pas sûr pourquoi c'est nécessaire ...


10
2017-11-21 16:44



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 faire ça.

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

Et je l'utilise comme ceci:

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

10
2018-06-01 15:12



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'il s'agissait de méthodes d'instance. Puisqu'une classe statique n'est pas instanciable, vous n'aurez jamais une instance de la classe pour 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, le nouveau T () est lent car il génère un appel à System.Activator qui utilise la réflexion pour obtenir le constructeur par défaut avant de l'appeler".

New () est compilé avec l'instruction IL "newobj" si le type est connu au moment de la compilation. Newobj prend un constructeur pour l'invocation directe. Appels à System.Activator.CreateInstance () compiler à l'instruction "appel" IL pour appeler System.Activator.CreateInstance (). New () lorsqu'il est utilisé contre des types génériques entraînera un appel à System.Activator.CreateInstance (). Le message de M. Obnoxious n'était pas clair sur ce point ... et bien, désagréable.

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 cet 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

7
2018-01-11 19:35



Vous ne pouvez pas ajouter statique méthodes à un type. Vous ne pouvez ajouter que des méthodes d'instance (pseudo) à une instance d'un type.

Le point de la this modificateur est de dire au compilateur C # de passer l'instance du côté gauche du . 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.


5
2017-10-30 04:00



J'ai essayé de le faire avec System.Environment lorsque j'apprenais des méthodes d'extension et que je n'avais 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.


4
2017-11-21 16:39



A partir de C # 7, ceci n'est pas supporté. Il y a cependant discussions sur l'intégration de quelque chose comme ça en C # 8 et propositions à soutenir.


4
2017-09-28 10:02