webentwicklung-frage-antwort-db.com.de

Kann ich einer vorhandenen statischen Klasse Erweiterungsmethoden hinzufügen?

Ich bin ein Fan von Erweiterungsmethoden in C #, hatte aber keinen Erfolg, wenn ich einer statischen Klasse wie Console eine Erweiterungsmethode hinzufügte.

Wenn ich beispielsweise der Konsole eine Erweiterung mit dem Namen "WriteBlueLine" hinzufügen möchte, gehen Sie wie folgt vor:

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

Ich habe es versucht, indem ich eine lokale, öffentliche statische Methode mit Console als 'this'-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();
    }
}

Damit wurde der Konsole keine WriteBlueLine-Methode hinzugefügt ... mache ich das falsch? Oder nach dem Unmöglichen fragen?

506
Leon Bambrick

Nein. Erweiterungsmethoden erfordern eine Instanzvariable (Wert) für ein Objekt. Sie können jedoch einen statischen Wrapper um die Schnittstelle ConfigurationManager 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" );
      }
 }
263
tvanfosson

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

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

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

So würden Sie das verwenden:

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

WARUM habe ich mich für den Aufruf des Standardkonstruktors als Beispiel entschieden und UND warum gebe ich nicht einfach im ersten Code-Snippet new T() zurück, ohne den gesamten Ausdrucks-Garbage auszuführen? Ihr Glückstag, weil Sie eine 2fer bekommen. Wie jeder fortgeschrittene .NET-Entwickler weiß, ist new T() langsam, weil es einen Aufruf an System.Activator generiert, der Reflection verwendet, um den Standardkonstruktor vorher zu erhalten Aber mein Code ruft den Standardkonstruktor des Objekts direkt auf.

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

89
Mr. Obnoxious

Es ist nicht möglich.

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

Ihre Entscheidung ist nicht sinnvoll und zwingt Programmierer, (wie oben beschrieben) eine sinnlose Wrapper-Klasse zu schreiben.

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

Die einzige Möglichkeit, dies zu tun, besteht darin, auf verschiedene Klassen zu verweisen oder einen Wrapper für Hunderte verschiedener 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 Unterteilen von Bibliotheken bleiben nicht bestehen, sobald Instanzen erweitert werden können.

45
Tom Deloford

Ich bin über diesen Thread gestolpert, als ich versucht habe, eine Antwort auf dieselbe Frage zu finden, die das OP hatte. Ich habe die gewünschte Antwort nicht gefunden, aber am Ende habe ich das getan.

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à");
19
Adel G.Eibesh

Möglicherweise können Sie eine statische Klasse mit Ihrem benutzerdefinierten Namespace und demselben Klassennamen 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);
        }
    }
}
17
Pag Sun
10
mbx

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

10
Will

Erweiterungsmethoden selbst sind statisch. Sie werden jedoch so aufgerufen, als handele es sich um Instanzmethoden. Da eine statische Klasse nicht instanziierbar ist, hätten Sie niemals eine Instanz der Klasse, von der aus Sie eine Erweiterungsmethode aufrufen könnten. Aus diesem Grund erlaubt der Compiler nicht, Erweiterungsmethoden für statische Klassen zu definieren.

Mr. Obnoxious schrieb: "Wie jeder fortgeschrittene .NET-Entwickler weiß, ist new T() langsam, da es einen Aufruf von System.Activator generiert, der Reflection verwendet, um den Standardkonstruktor abzurufen, bevor er aufgerufen wird." .

New () wird zur IL-Anweisung "newobj" kompiliert, wenn der Typ zum Kompilierungszeitpunkt bekannt ist. Newobj verwendet einen Konstruktor für den direkten Aufruf. Aufrufe von System.Activator.CreateInstance () werden mit der IL-Anweisung "call" kompiliert, um System.Activator.CreateInstance () aufzurufen. Wenn New () für generische Typen verwendet wird, wird System.Activator.CreateInstance () aufgerufen. Der Beitrag von Mr. Obnoxious war in diesem Punkt unklar ... und nun, widerlich.

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
7
Brian Griffin

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

Mit dem Modifikator this soll der C # -Compiler angewiesen werden, die Instanz auf der linken Seite des . als erster Parameter der static/extension-Methode.

Wenn Sie einem Typ statische Methoden hinzufügen, muss für den ersten Parameter keine Instanz übergeben werden.

5
Brannon

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

4
Robert S.

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

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 Klassen aufrufen. Wenn die anderen Klassen auf die anderen statischen Funktionen von Console zugreifen möchten, müssen sie über ihren Namespace explizit referenziert werden.

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

Also hättest du sowas gerne

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 liefern, nach der Sie suchen.

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

2
Douglas Potesta

ja, in begrenztem Sinne.

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

Dies funktioniert, die Konsole jedoch nicht, da 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);           
    }
}

Das funktioniert, solange es sich nicht im selben Namespace befindet. Das Problem ist, dass Sie für jede Methode in System.Console eine proxystatische Methode schreiben müssen. Es ist nicht unbedingt eine schlechte Sache, wie Sie so 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 und Weise, wie es funktioniert, ist, dass Sie etwas in die Standard-WriteLine einbinden. Es kann sich um eine Zeilenanzahl oder einen schlechten Word-Filter handeln. Wenn Sie in Ihrem Namespace nur Console angeben, beispielsweise WebProject1, und das Namespace-System importieren, wird für diese Klassen im Namespace WebProject1 WebProject1.Console anstelle von System.Console als Standard ausgewählt. Mit diesem Code werden alle Console.WriteLine-Aufrufe insofern blau, als Sie nie System.Console.WriteLine angegeben haben.

1
Black Dog

Das Folgende wurde als edit zu Tvanfossons Antwort abgelehnt. Ich wurde gebeten, es als meine eigene Antwort beizutragen. Ich habe seinen Vorschlag verwendet und die Implementierung eines Wrappers ConfigurationManager abgeschlossen. Im Prinzip habe ich einfach das ... in tvanfossons antwort.

Nein. Erweiterungsmethoden erfordern eine Instanz eines Objekts. Sie können jedoch einen statischen Wrapper um die ConfigurationManager-Oberfläche 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);
    }
}
1

Sie können eine Besetzung für null verwenden, damit es funktioniert.

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