c# - être - Puis-je ajouter des méthodes d'extension à une classe statique existante?




méthode d'extension c# (10)

À partir de C # 7, ceci 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 .

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 la console.

Par exemple, si je veux ajouter une extension à la console, appelée 'WriteBlueLine', afin que je puisse 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é une méthode 'WriteBlueLine' à la console ... est-ce que je le fais mal? Ou demander l'impossible?


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 tests MS Unit statique Assert: Je veux 1 méthode 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 de ne pas autoriser les extensions statiques. Les arguments sur la section des bibliothèques ne se tiennent pas une fois que les instances peuvent être étendues.


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. Comme une classe statique n'est pas instanciable, vous n'avez 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

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, parce que les méthodes d'extension nécessitent une instance de la classe.


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


Non. Les méthodes d'extension nécessitent une variable d'instance (valeur) pour un objet. 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 puisque 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" );
      }
 }

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 écrire techniquement des méthodes d'extension statiques, ce code exploite plutôt une échappatoire dans les méthodes d'extension. Cette lacune étant que vous pouvez appeler des méthodes d'extension sur des objets NULL sans avoir 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(...)

Pourquoi ai-je choisi d'appeler le constructeur par défaut comme exemple, et ET pourquoi ne pas simplement retourner un nouveau T () dans le premier extrait de code sans faire tout de cette ordure Expression? Eh bien aujourd'hui votre jour de chance parce que vous obtenez un 2fer. Comme tout développeur avancé de .NET 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. Merde vous Microsoft! Cependant, mon code appelle directement le constructeur par défaut de l'objet.

Les extensions statiques seraient mieux que cela mais les temps désespérés appellent des mesures désespérées.


Vous POUVEZ le faire si vous êtes prêt à "frig" un peu en faisant une variable de la classe statique et en l'assignant à null. Cependant, cette méthode ne serait pas disponible pour les appels statiques sur la classe, donc je ne sais pas à quel point cela serait utile:

Console myConsole = null;
myConsole.WriteBlueLine("my blue line");

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

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 { }

oui, dans un sens limité.

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

Cela fonctionne mais la console ne le fait pas parce que c'est statique.

public static class Console
{       
    public static void WriteLine(String x)
    { System.Console.WriteLine(x); }

    public static void WriteBlueLine(String 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 statique proxy pour chaque méthode que System.Console a. Ce n'est pas nécessairement une mauvaise chose car vous pouvez ajouter quelque chose comme ceci:

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

ou

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

La façon dont cela fonctionne est que vous accrochez quelque chose dans le WriteLine standard. Ce pourrait être un compte de ligne ou un mauvais filtre de mot ou n'importe quoi. Chaque fois que vous spécifiez simplement Console dans votre espace de noms, dites 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. Ainsi, ce code transformera tous les appels Console.WriteLine en bleu dans la mesure où vous n'avez jamais spécifié System.Console.WriteLine.







extension-methods