Puis-je ajouter des méthodes d'extension à une classe statique existante?
je suis un fan des méthodes d'extension en C#, mais je n'ai pas eu de succès en ajoutant 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", pour que je puisse aller:
Console.WriteBlueLine("This text is blue");
j'ai essayé en ajoutant une méthode statique locale, Publique, avec la Console comme paramètre 'ceci'... 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é un "WriteBlueLine" méthode pour consoler... suis-je le fais mal? Ou demander l'impossible?
15 réponses
Pas de. Les méthodes d'Extension nécessitent une variable d'instance (valeur) pour un objet. Vous pouvez cependant écrire une enveloppe 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 dans 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 ça marche. Alors que vous ne pouvez pas techniquement écrire des méthodes d'extension statiques, ce code exploite plutôt une faille dans les méthodes d'extension. Cette faille est que vous pouvez appeler des méthodes d'extension sur les objets null sans obtenir l'exception null (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 pourquoi ne retourne-t-on pas new T() dans le premier extrait de code sans faire toute cette expression garbage? Aujourd'hui, c'est ton jour de chance parce que tu as un 2. Comme tout développeur avancé. net le sait, new T () est lent car il génère un appel au système.Activateur qui utilise la réflexion pour obtenir le constructeur par défaut avant de l'appeler. Putain vous Microsoft! Cependant mon code appelle le constructeur par défaut de l'objet directement.
Statique extensions serait mieux que cela, mais les temps désespérés appellent des mesures désespérées.
C'est impossible.
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 de wrapper inutile.
Ici est un bon exemple: en train de s'étendre statique MS tests Unitaires de la classe Assert: je veux 1 plus méthode Assert AreEqual(x1,x2)
.
la seule façon de le faire est de pointer vers des classes différentes ou d'écrire un wrapper environ 100s de différentes méthodes D'Assert. pourquoi!?
si la décision a été prise de permettre des prolongations de cas, je ne vois aucune raison logique de ne pas permettre des prolongations statiques. Les arguments sur le découpage des bibliothèques ne se lèvent pas une fois que les instances peuvent être étendues.
peut-être pourriez-vous 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);
}
}
}
je suis tombé sur ce fil en essayant de trouver une réponse à la même question que L'OP avait. Je n'ai pas trouvé la réponse que je voulais, mais j'ai fini par faire.
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à");
Non. Les définitions de méthode d'Extension nécessitent une instance du type que vous étendez. Son malheureux; je ne sais pas pourquoi sa nécessaire...
comme pour les méthodes d'extension, les méthodes d'extension elles-mêmes sont statiques; mais elles sont invoquées comme si elles étaient des méthodes d'instance. Depuis une classe statique n'est pas instanciable, vous n'auriez jamais une instance de la classe pour appeler une méthode d'extension. Pour cette raison, le compilateur ne permet pas de définir des méthodes d'extension pour les classes statiques.
M. odieux a écrit: "comme tout développeur avancé. Système.Activator qui utilise la réflexion pour obtenir le constructeur par défaut avant de l'appeler".
New () est compilé dans l'instruction IL "newobj" si le type est connu au moment de la compilation. Newobj prend un constructeur pour l'invocation directe. Les appels Système.Activateur.CreateInstance () compiler à L'instruction IL "call" pour invoquer le système.Activateur.CreateInstance(). New () lorsqu'il est utilisé contre des types génériques donnera lieu à un appel au système.Activateur.CreateInstance(). Le post de M. Odieux 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 CE 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 ajouter méthodes statiques à un type. Vous pouvez seulement ajouter des méthodes d'instance (pseudo -) à une instance d'un type.
le point du modificateur this
est de dire au compilateur C# de passer l'instance du côté gauche du .
comme premier paramètre de la méthode statique/extension.
dans le cas d'ajouter des méthodes statiques à un type, il n'y a pas d'instance à passer pour le premier paramètre.
de C#7 ce n'est pas pris en charge. Il y a cependant discussions sur l'intégration de quelque chose comme cela dans C#8 et propositions qui méritent d'être soutenues .
j'ai essayé de le faire avec le Système.Environnement à l'époque où j'apprenais les méthodes de vulgarisation et que je n'ai pas réussi. La raison est, comme d'autres le mentionnent, parce que les méthodes d'extension nécessitent une instance de la classe.
oui, dans un sens restreint.
public class DataSet : System.Data.DataSet
{
public static void SpecialMethod() { }
}
cela fonctionne mais la Console ne fonctionne pas parce qu'elle 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 qu'il n'est pas sur le même espace de noms. Le problème est que vous devez écrire une méthode statique de proxy pour chaque méthode que le système.Console ont. Ce n'est pas nécessairement une mauvaise chose que 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 L'écriture standard. Ça pourrait être un nombre de lignes ou un filtre de mauvais mots ou autre. Chaque fois que vous spécifiez juste la Console dans votre espace de noms dire WebProject1 et importer le système d'espace de noms, WebProject1.La Console sera choisie par rapport au système.Console par défaut pour les classes dans namespace WebProject1. Donc ce code va faire tourner toute la Console.WriteLine appelle en bleu dans la mesure où vous n'avez jamais spécifié le système.Console.WriteLine.
ce qui suit a été rejeté en tant que modifier à la réponse de tvanfosson. On m'a demandé de le faire comme ma propre réponse. J'ai utilisé sa suggestion et j'ai terminé la mise en place d'une enveloppe ConfigurationManager
. En principe, j'ai simplement rempli le ...
dans la réponse de tvanfosson.
Pas de. Les méthodes d'Extension nécessitent une instance d'un objet. Vous pouvez cependant, écrivez une enveloppe statique autour de la ConfigurationManager interface. Si vous implémentez le wrapper, vous n'avez pas besoin d'extension méthode puisque vous pouvez juste ajouter la méthode directement.
public static class ConfigurationManagerWrapper
{
public static NameValueCollection AppSettings
{
get { return ConfigurationManager.AppSettings; }
}
public static ConnectionStringSettingsCollection ConnectionStrings
{
get { return ConfigurationManager.ConnectionStrings; }
}
public static object GetSection(string sectionName)
{
return ConfigurationManager.GetSection(sectionName);
}
public static Configuration OpenExeConfiguration(string 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(string sectionName)
{
ConfigurationManager.RefreshSection(sectionName);
}
}
vous pouvez utiliser un plâtre sur null pour le faire fonctionner.
public static class YoutTypeExtensionExample
{
public static void Example()
{
((YourType)null).ExtensionMethod();
}
}
de L'extension:
public static class YourTypeExtension
{
public static void ExtensionMethod(this YourType x) { }
}
YourType:
public class YourType { }
Il n'est pas possible d'écrire une extension de la méthode, cependant, il est possible d'imiter le comportement que vous demandez.
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
}
cela vous permettra d'appeler la 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 par leur espace de noms.
vous pouvez toujours ajouter toutes les méthodes dans la classe de remplacement si vous voulez avoir tous en un seul endroit.
donc vous auriez quelque chose comme
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
public static void WriteLine(string text)
{
FooConsole.WriteLine(text);
}
...etc.
}
Ce serait le genre de comportement que vous recherchez.
* Note Console devra être ajouté à travers l'espace de noms dans lequel vous l'avez mis.
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 pas sûr de combien d'utilisation il serait:
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();
}
}