Kann ich Erweiterungsmethoden zu einer vorhandenen statischen class hinzufügen?

Ich bin ein Fan von Erweiterungsmethoden in C #, habe aber keine Erfolge beim Hinzufügen einer Erweiterungsmethode zu einer statischen class, z. B. Konsole, erzielt.

Zum Beispiel, wenn ich Console eine Erweiterung hinzufügen möchte, ‘WriteBlueLine’ genannt, damit ich gehen kann:

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

Ich habe das versucht, indem ich eine lokale, öffentliche statische Methode mit Console als ‘diesen’ Parameter hinzugefügt habe … aber keine Würfel!

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

Dies hat der Konsole keine ‘WriteBlueLine’-Methode hinzugefügt … mache ich das falsch? Oder nach dem Unmöglichen fragen?

   

Nein. Erweiterungsmethoden benötigen eine Instanzvariable (Wert) für ein Objekt. Sie können jedoch einen statischen Wrapper um die ConfigurationManager Schnittstelle schreiben. Wenn Sie den Wrapper implementieren, benötigen Sie keine Erweiterungsmethode, da Sie die Methode einfach direkt hinzufügen können.

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

Können Sie classn in C # statische Erweiterungen hinzufügen? Nein, aber Sie können das tun:

 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))).Compile(); } public static Func Create { get; private set; } } 

So funktioniert das. Sie können zwar keine statischen Erweiterungsmethoden schreiben, stattdessen nutzt dieser Code eine Lücke in Erweiterungsmethoden. Diese Lücke besteht darin, dass Sie Erweiterungsmethoden für Nullobjekte aufrufen können, ohne die Null-Ausnahme zu erhalten (es sei denn, Sie greifen auf etwas über @ this zu).

Also hier ist, wie Sie das verwenden würden:

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

Nun, WARUM habe ich gewählt, den Standardkonstruktor als Beispiel aufzurufen, und UND, warum gebe ich nicht einfach T () in das erste Code-Snippet zurück, ohne all diesen Ausdrucksmüll zu machen? Nun, heute ist dein Glückstag, weil du eine 2er bekommst. Wie jeder fortgeschrittene .NET-Entwickler weiß, ist new T () langsam, weil es einen Aufruf von System.Activator generiert, der Reflektion verwendet, um den Standardkonstruktor vor dem Aufruf zu erhalten. Verdammt, Microsoft! Mein Code ruft jedoch den Standardkonstruktor des Objekts direkt auf.

Statische Erweiterungen wären besser, aber verzweifelte Zeiten erfordern verzweifelte Maßnahmen.

Es ist nicht möglich.

Und ja, ich denke MS hat hier einen Fehler gemacht.

Ihre Entscheidung macht keinen Sinn und zwingt Programmierer dazu, (wie oben beschrieben) eine sinnlose Wrapper-class zu schreiben.

Hier ist ein gutes Beispiel: Versuch, statische MS-Unit-Test-class zu erweitern. Assert: Ich möchte noch 1 Assert-Methode AreEqual(x1,x2) .

Der einzige Weg, dies zu tun, ist, auf verschiedene classn zu zeigen oder einen Wrapper um 100 verschiedene Assert-Methoden zu schreiben. Warum!?

Wenn die Entscheidung getroffen wurde, Erweiterungen von Instanzen zuzulassen, sehe ich keinen logischen Grund, statische Erweiterungen nicht zuzulassen. Die Argumente zum Aufteilen von Bibliotheken stehen nicht mehr zur Verfügung, sobald Instanzen erweitert werden können.

Vielleicht könnten Sie eine statische class mit Ihrem benutzerdefinierten Namespace und demselben classnnamen hinzufügen:

 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); } } } 

Ich stolperte über diesen Thread, während ich versuchte, eine Antwort auf die gleiche Frage zu finden, die das OP hatte. Ich habe nicht die Antwort gefunden, die ich wollte, aber ich bin damit fertig geworden.

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

Und ich benutze es so:

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

Nee. Definitionen für Erweiterungsmethoden erfordern eine Instanz des Typs, den Sie erweitern. Es ist unglücklich; Ich bin mir nicht sicher, warum es erforderlich ist …

Wie bei den Erweiterungsmethoden sind die Erweiterungsmethoden selbst statisch; aber sie werden aufgerufen, als wären sie Instanzmethoden. Da eine statische class nicht instanziierbar ist, hätten Sie niemals eine Instanz der class, von der eine Erweiterungsmethode aufgerufen werden kann. Aus diesem Grund erlaubt der Compiler keine Erweiterungsmethoden für statische classn.

Herr Obnoxious schrieb: “Wie jeder fortgeschrittene .NET-Entwickler weiß, ist new T () langsam, weil es einen Aufruf von System.Activator erzeugt, der Reflektion verwendet, um den Standardkonstruktor zu erhalten, bevor er aufgerufen wird”.

New () wird in die IL-statement “newobj” kompiliert, wenn der Typ zum Zeitpunkt der Kompilierung bekannt ist. Newobj nimmt einen Konstruktor für den direkten Aufruf. Aufrufe an System.Activator.CreateInstance () kompilieren zu der IL “call” statement, um System.Activator.CreateInstance () aufzurufen. Wenn New () für generische Typen verwendet wird, wird ein Aufruf von System.Activator.CreateInstance () ausgetriggers. Der Beitrag von Mr. Obnoxious war in diesem Punkt unklar … und nun, anstößig.

Dieser Code:

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

produziert diese 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 

Sie können einem Typ keine statischen Methoden hinzufügen. Sie können einer Instanz eines Typs nur (Pseudo-) Instanzmethoden hinzufügen.

Der Punkt this Modifikators besteht darin, dem C # -Compiler mitzuteilen, dass er die Instanz auf der linken Seite des . als der erste Parameter der statischen / Erweiterungsmethode.

Wenn einem Typ statische Methoden hinzugefügt werden, gibt es keine Instanz für den ersten Parameter.

Ab C # 7 wird dies nicht unterstützt. Es gibt jedoch Diskussionen darüber, so etwas in C # 8 zu integrieren und Vorschläge, die es zu unterstützen gilt .

Ich habe versucht, dies mit System.Environment zurück zu tun, als ich Erweiterungsmethoden lernte und nicht erfolgreich war. Der Grund ist, wie andere erwähnen, weil Erweiterungsmethoden eine Instanz der class erfordern.

Ja, in einem begrenzten Sinne.

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

Das funktioniert, aber die Konsole nicht, weil sie statisch ist.

 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); } } 

Dies funktioniert, solange es nicht im selben Namespace ist. Das Problem ist, dass Sie eine statische Proxy-Methode für jede Methode schreiben müssen, die System.Console hat. Es ist nicht unbedingt eine schlechte Sache, da Sie etwas hinzufügen können:

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

oder

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

Die Art, wie es funktioniert, ist, dass Sie etwas in die Standard WriteLine einhaken. Es könnte ein Zeilenzähler oder ein schlechter Wortfilter oder was auch immer sein. Wenn Sie in Ihrem Namespace einfach Console angeben, also WebProject1, und den Namespace System importieren, wird WebProject1.Console als Standard für diese classn im Namespace WebProject1 über System.Console ausgewählt. Daher wird dieser Code alle Aufrufe von Console.WriteLine in Blau umwandeln, sofern Sie nicht System.Console.WriteLine angegeben haben.

Das Folgende wurde als Bearbeitung von tvanfossons Antwort abgelehnt. Ich wurde gebeten, es als meine eigene Antwort beizutragen. Ich habe seinen Vorschlag verwendet und die Implementierung eines ConfigurationManager Wrappers abgeschlossen. Im Prinzip habe ich einfach die ... in Tvanfossons Antwort ausgefüllt.

Nein. Erweiterungsmethoden erfordern eine Instanz eines Objekts. Sie können jedoch einen statischen Wrapper um die ConfigurationManager-Schnittstelle schreiben. Wenn Sie den Wrapper implementieren, benötigen Sie keine Erweiterungsmethode, da Sie die Methode einfach direkt hinzufügen können.

 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); } } 

Sie können eine Umwandlung von null verwenden, um es zu funktionieren.

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

Die Erweiterung:

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

Dein Typ:

 public class YourType { } 

Es ist nicht möglich, eine Erweiterungsmethode zu schreiben, es ist jedoch möglich, das von Ihnen gewünschte Verhalten zu imitieren.

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

Auf diese Weise können Sie Console.WriteBlueLine (fooText) in anderen classn aufrufen. Wenn die anderen classn Zugriff auf die anderen statischen functionen von Console benötigen, müssen sie explizit über ihren Namespace referenziert werden.

Sie können immer alle Methoden der Ersetzungsklasse hinzufügen, wenn Sie alle an einem Ort haben möchten.

So hättest du so etwas wie

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

Dies würde die Art von Verhalten bieten, nach der Sie suchen.

* Hinweis Die Konsole muss über den Namespace hinzugefügt werden, in den Sie sie eingefügt haben.

Sie können dies tun, wenn Sie bereit sind, es ein wenig zu “frigieren”, indem Sie eine Variable der statischen class erstellen und sie null zuweisen. Diese Methode ist jedoch für statische Aufrufe der class nicht verfügbar, also nicht sicher, wie viel sie verwenden würde:

 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(); } }