Sovraccarico dell'operatore con metodi di estensione C #


174

Sto tentando di utilizzare metodi di estensione per aggiungere un sovraccarico dell'operatore alla StringBuilderclasse C # . In particolare, dato StringBuilder sb, vorrei sb += "text"diventare equivalente a sb.Append("text").

Ecco la sintassi per la creazione di un metodo di estensione per StringBuilder:

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

Aggiunge correttamente il blahmetodo di estensione a StringBuilder.

Sfortunatamente, il sovraccarico dell'operatore non sembra funzionare:

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

Tra le altre questioni, la parola chiave thisnon è consentita in questo contesto.

È possibile aggiungere sovraccarichi dell'operatore tramite metodi di estensione? In tal caso, qual è il modo corretto di procedere?


4
Anche se all'inizio sembra un'idea interessante, considera var otherSb = sb + "hi";
ascia di guerra - fatto con SOverflow l'

Risposte:


150

Questo non è attualmente possibile, poiché i metodi di estensione devono essere in classi statiche e le classi statiche non possono avere sovraccarichi dell'operatore.

Mads Torgersen, C # Language PM dice:

... per la versione Orcas abbiamo deciso di adottare un approccio cauto e di aggiungere solo metodi di estensione regolari, al contrario di proprietà di estensione, eventi, operatori, metodi statici, ecc. ecc. I metodi di estensione regolari erano ciò di cui avevamo bisogno per LINQ, e avevano un design sintatticamente minimale che non poteva essere facilmente imitato per alcuni degli altri membri.

Stiamo diventando sempre più consapevoli che altri tipi di membri dell'estensione potrebbero essere utili e quindi torneremo su questo problema dopo Orcas. Nessuna garanzia, però!

Modificare:

Ho appena notato, Mads ha scritto di più nello stesso articolo :

Mi dispiace segnalare che non lo faremo nella prossima versione. Abbiamo preso molto sul serio i membri delle estensioni nei nostri piani e abbiamo speso molto sforzo nel tentativo di farli nel modo giusto, ma alla fine non siamo riusciti a farlo abbastanza agevolmente e abbiamo deciso di cedere il passo ad altre funzionalità interessanti.

Questo è ancora sul nostro radar per le versioni future. Ciò che sarà di aiuto è se otteniamo una buona quantità di scenari avvincenti che possono aiutare a guidare la progettazione giusta.


Questa funzionalità è attualmente disponibile (potenzialmente) per C # 8.0. Mads parla un po 'di più sull'implementazione qui .


Da allora questa pagina è stata rimossa; questo problema non è ancora stato risolto.
Chris Moschini,

17
Peccato. Volevo solo aggiungere un operatore per moltiplicare un TimeSpan per un valore scalare ... :(
Filip Skakun

Speravo di implementare questo stesso concetto per trasmettere Stringun PowerShell ScriptBlock.
Trevor Sullivan,

Ciò significa che non posso sovraccaricare% per essere xo tra booleani? :( morto true.Xor(false)allora
SparK

3
@SparK ^è l'operatore xor in C #
Jacob Krall,

57

Se controlli i luoghi in cui desideri utilizzare questo "operatore di estensione" (che normalmente esegui comunque con i metodi di estensione), puoi fare qualcosa del genere:

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

} 

La StringBuilderWrapperclasse dichiara un operatore di conversione implicito da a StringBuilder e dichiara l' +operatore desiderato . In questo modo, aStringBuilder possibile passare a ReceiveImportantMessage, che verrà silenziosamente convertito in a StringBuilderWrapper, dove è +possibile utilizzare l' operatore.

Per rendere questo fatto più trasparente per i chiamanti, è possibile dichiarare ReceiveImportantMessage come prendere un StringBuildere usare semplicemente un codice come questo:

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

Oppure, per usarlo in linea dove stai già utilizzando a StringBuilder , puoi semplicemente farlo:

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

Ho creato un post sull'utilizzo di un approccio simile per renderlo IComparablepiù comprensibile.


2
@Leon: intendevo davvero comporlo, non ereditarlo. Ad ogni modo, non ho potuto ereditarlo poiché è sigillato.
Jordão,

5
@Leon: questo è il cuore di questa tecnica. Posso farlo perché è stato dichiarato un operatore di conversione implicitoStringBuilderWrapper che lo rende possibile.
Jordão,

1
@pylover: hai ragione, questo richiede la creazione di un nuovo tipo, che avvolgerà il StringBuildertipo e fornirà un operatore di conversione implicito da esso. Successivamente, può essere utilizzato con valori letterali di stringa, come dimostrato nell'esempio:sb += "Hello World!";
Jordão

2
Posso suggerire quindi di aggiungere un metodo di estensione a String: PushIndent(" ".X(4))(potrebbe anche essere chiamato Times). O forse con questo costruttore: PushIndent(new String(' ', 4)).
Jordão,

1
@Jordão: ottima risposta;)
Vinicius

8

Sembra che questo non sia attualmente possibile - c'è un problema di feedback aperto che richiede questa funzionalità su Microsoft Connect:

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

suggerendo che potrebbe apparire in una versione futura ma non è implementato per la versione corrente.


Cosa intendi esattamente con "non è attualmente possibile?" Deve essere possibile nel CLR perché F # supporta l'estensione tutto.
Matthew Olenik,

1
Penso che significhi che non è possibile in C #, non nel CLR. L'intera cosa dei metodi di estensione è comunque un trucco del compilatore C #.
tofi9

1
Il link è morto ora.
CBHacking

1

Sebbene non sia possibile eseguire gli operatori, è sempre possibile creare i metodi Aggiungi (o Concat), Sottrai e Confronta ....

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


}

1

Hah! Stavo cercando "sovraccarico dell'operatore di estensione" con esattamente lo stesso desiderio, per sb + = (cosa).

Dopo aver letto le risposte qui (e visto che la risposta è "no"), per le mie esigenze particolari, sono andato con un metodo di estensione che combina sb.AppendLine e sb.AppendFormat e sembra più ordinato di entrambi.

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

}

E così,

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

Non è una risposta generale, ma può essere di interesse per i futuri ricercatori che guardano questo tipo di cose.


4
Penso che il tuo metodo di estensione leggerebbe meglio se lo chiamassi AppendLineinvece di Line.
David,

0

È possibile rigg con un involucro ed estensioni ma impossibile farlo correttamente. Finisci con la spazzatura che sconfigge totalmente lo scopo. Ho un post qui da qualche parte che lo fa, ma è inutile.

A proposito, tutte le conversioni numeriche creano immondizia nel generatore di stringhe che deve essere corretto. Ho dovuto scrivere un wrapper per quello che funziona e lo uso. Vale la pena esaminarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.