Überladen des Operators mit C # -Erweiterungsmethoden

174

Ich versuche, Erweiterungsmethoden zu verwenden, um der C # StringBuilder-Klasse eine Operaterüberladung hinzuzufügen . Insbesondere StringBuilder sbwürde ich gerne sb += "text"gleichwertig werden mit sb.Append("text").

Hier ist die Syntax zum Erstellen einer Erweiterungsmethode für StringBuilder:

public static class sbExtensions
{
    public static StringBuilder blah(this StringBuilder sb)
    {
        return sb;
    }
} 

Die blahErweiterungsmethode wird erfolgreich zum hinzugefügt StringBuilder.

Leider scheint die Überlastung des Bedieners nicht zu funktionieren:

public static class sbExtensions
{
    public static StringBuilder operator +(this StringBuilder sb, string s)
    {
        return sb.Append(s);
    }
} 

Unter anderem ist das Schlüsselwort thisin diesem Zusammenhang nicht zulässig.

Ist das Hinzufügen von Operatorüberladungen über Erweiterungsmethoden möglich? Wenn ja, wie geht man richtig vor?

Jude Allred
quelle
4
Obwohl dies auf den ersten Blick eine coole Idee zu sein scheint, betrachten Sie var otherSb = sb + "hi";
Beil - fertig mit SOverflow

Antworten:

150

Dies ist derzeit nicht möglich, da Erweiterungsmethoden in statischen Klassen vorliegen müssen und statische Klassen keine Operatorüberladungen aufweisen dürfen.

Mads Torgersen, PM in C # -Sprache, sagt:

... für die Orcas-Version haben wir uns für einen vorsichtigen Ansatz entschieden und nur reguläre Erweiterungsmethoden hinzugefügt, im Gegensatz zu Erweiterungseigenschaften, Ereignissen, Operatoren, statischen Methoden usw. usw. Regelmäßige Erweiterungsmethoden waren das, was wir für LINQ benötigten, und das hatten sie auch Ein syntaktisch minimales Design, das für einige der anderen Mitgliedsarten nicht einfach nachgeahmt werden konnte.

Wir werden uns zunehmend bewusst, dass andere Arten von Erweiterungsmitgliedern nützlich sein könnten, und werden daher nach Orcas auf dieses Problem zurückkommen. Keine Garantie!

Bearbeiten:

Mir ist gerade aufgefallen, dass Mads im selben Artikel mehr geschrieben hat :

Es tut mir leid zu berichten, dass wir dies in der nächsten Version nicht tun werden. Wir haben Erweiterungsmitglieder in unseren Plänen sehr ernst genommen und viel Mühe darauf verwendet, sie richtig zu machen, aber am Ende konnten wir es nicht reibungslos genug machen und beschlossen, anderen interessanten Funktionen Platz zu machen.

Dies ist immer noch auf unserem Radar für zukünftige Versionen. Was helfen wird, ist, wenn wir eine gute Anzahl überzeugender Szenarien erhalten, die dazu beitragen können, das richtige Design zu entwickeln.


Diese Funktion ist derzeit (möglicherweise) für C # 8.0 verfügbar. Mads spricht ein bisschen mehr darüber die Umsetzung hier .

Jacob Krall
quelle
Diese Seite wurde inzwischen entfernt. Dieses Problem ist immer noch nicht behoben.
Chris Moschini
17
Schade. Ich wollte nur einen Operator hinzufügen, um eine Zeitspanne mit einem Skalarwert zu multiplizieren ... :(
Filip Skakun
Ich hatte gehofft, dasselbe Konzept zu implementieren, Stringum eine PowerShell zu übertragen ScriptBlock.
Trevor Sullivan
Das heißt, ich kann% nicht überladen, um zwischen Booleschen Werten zu sein? :( tot true.Xor(false)dann
SPARK
3
@ ParK ^ist der xor-Operator in C #
Jacob Krall
57

Wenn Sie die Stellen steuern, an denen Sie diesen "Erweiterungsoperator" verwenden möchten (was Sie normalerweise sowieso mit Erweiterungsmethoden tun), können Sie Folgendes tun:

class Program {

  static void Main(string[] args) {
    StringBuilder sb = new StringBuilder();
    ReceiveImportantMessage(sb);
    Console.WriteLine(sb.ToString());
  }

  // the important thing is to use StringBuilderWrapper!
  private static void ReceiveImportantMessage(StringBuilderWrapper sb) {
    sb += "Hello World!";
  }

}

public class StringBuilderWrapper {

  public StringBuilderWrapper(StringBuilder sb) { StringBuilder = sb; }
  public StringBuilder StringBuilder { get; private set; }

  public static implicit operator StringBuilderWrapper(StringBuilder sb) {
    return new StringBuilderWrapper(sb);
  }

  public static StringBuilderWrapper operator +(StringBuilderWrapper sbw, string s) { 
      sbw.StringBuilder.Append(s);
      return sbw;
  }

} 

Die StringBuilderWrapperKlasse deklariert einen impliziten Konvertierungsoperator von a StringBuilder und deklariert den gewünschten +Operator. Auf diese Weise StringBuilderkann a an übergeben werden ReceiveImportantMessage, das stillschweigend in a konvertiert wird StringBuilderWrapper, wo der +Operator verwendet werden kann.

Um diese Tatsache für Anrufer transparenter zu machen, können Sie deklarieren, dass Sie einen ReceiveImportantMessagenehmen StringBuilderund einfach Code wie diesen verwenden:

  private static void ReceiveImportantMessage(StringBuilder sb) {
    StringBuilderWrapper sbw = sb;
    sbw += "Hello World!";
  }

Um es inline zu verwenden, wo Sie bereits ein verwenden StringBuilder, können Sie dies einfach tun:

 StringBuilder sb = new StringBuilder();
 StringBuilderWrapper sbw = sb;
 sbw += "Hello World!";
 Console.WriteLine(sb.ToString());

Ich habe einen Beitrag über einen ähnlichen Ansatz erstellt, um ihn IComparableverständlicher zu machen .

Jordão
quelle
2
@Leon: Ich wollte es wirklich komponieren, nicht davon erben. Jedenfalls konnte ich nichts davon erben, da es versiegelt ist.
Jordão
5
@Leon: Das ist das Herzstück dieser Technik. Ich kann das tun, weil ein impliziter Konvertierungsoperator deklariert ist StringBuilderWrapper, der dies ermöglicht.
Jordão
1
@pylover: Sie haben Recht, dies erfordert das Erstellen eines neuen Typs, der den StringBuilderTyp umschließt und einen impliziten Konvertierungsoperator daraus bereitstellt. Danach kann es mit String-Literalen verwendet werden, wie im folgenden Beispiel gezeigt:sb += "Hello World!";
Jordão
2
Darf ich dann vorschlagen, eine Erweiterungsmethode hinzuzufügen zu String: PushIndent(" ".X(4))(könnte auch aufgerufen werden Times). Oder vielleicht mit diesem Konstruktor : PushIndent(new String(' ', 4)).
Jordão
1
@ Jordão: Tolle Antwort;)
Vinicius
8

Es scheint, dass dies derzeit nicht möglich ist. Es gibt ein offenes Feedback-Problem, bei dem genau diese Funktion in Microsoft Connect angefordert wird:

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=168224

Dies deutet darauf hin, dass es möglicherweise in einer zukünftigen Version erscheint, aber für die aktuelle Version nicht implementiert ist.

Dylan Beattie
quelle
Was genau meinst du mit "ist derzeit nicht möglich?" Es muss in der CLR möglich sein, da F # die Erweiterung alles unterstützt.
Matthew Olenik
1
Ich denke, er meint, dass es in C # nicht möglich ist, nicht in der CLR. Die ganze Sache mit den Erweiterungsmethoden ist sowieso ein C # -Compilertrick.
tofi9
1
Link ist jetzt tot.
CBHacking
1

Obwohl es nicht möglich ist, die Operatoren auszuführen, können Sie immer nur die Methoden Add (oder Concat), Subtract und Compare erstellen.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;    

namespace Whatever.Test
{
    public static class Extensions
    {
        public static int Compare(this MyObject t1, MyObject t2)
        {
            if(t1.SomeValueField < t2.SomeValueField )
                return -1;
            else if (t1.SomeValueField > t2.SomeValueField )
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }

        public static MyObject Add(this MyObject t1, MyObject t2)
        {
            var newObject = new MyObject();
            //do something  
            return newObject;

        }

        public static MyObject Subtract(this MyObject t1, MyObject t2)
        {
            var newObject= new MyObject();
            //do something
            return newObject;    
        }
    }


}
Chuck Rostance
quelle
1

Hah! Ich habe mit genau dem gleichen Wunsch nach "jb + = (thing)" "Extension Operator Overloading" gesucht.

Nachdem ich die Antworten hier gelesen hatte (und festgestellt hatte, dass die Antwort "Nein" ist), entschied ich mich für eine spezielle Erweiterungsmethode, die sb.AppendLine und sb.AppendFormat kombiniert und ordentlicher aussieht als beide.

public static class SomeExtensions
{
    public static void Line(this StringBuilder sb, string format, params object[] args)
    {
        string s = String.Format(format + "\n", args);
        sb.Append(s);
    }

}

Und so,

sb.Line("the first thing is {0}",first);
sb.Line("the second thing is {0}", second);

Keine allgemeine Antwort, kann aber für zukünftige Suchende von Interesse sein, die sich mit solchen Dingen befassen.

David Van Rand
quelle
4
Ich denke, Ihre Erweiterungsmethode würde besser lesen, wenn Sie sie AppendLineanstelle von benennen würden Line.
DavidRR
0

Es ist möglich, es mit einem Wrapper und Erweiterungen auszurüsten, aber es ist unmöglich, es richtig zu machen. Sie enden mit Müll, der den Zweck völlig zunichte macht. Ich habe irgendwo hier oben einen Beitrag, der das macht, aber es ist wertlos.

Übrigens: Bei allen numerischen Konvertierungen entsteht im String Builder Müll, der behoben werden muss. Ich musste einen Wrapper für das schreiben, was funktioniert und ich benutze ihn. Das ist es wert, durchgesehen zu werden.

wird motil
quelle