Sobrecarga del operador con métodos de extensión de C #

174

Estoy intentando usar métodos de extensión para agregar una sobrecarga de operadores a la StringBuilderclase C # . Específicamente, dado StringBuilder sb, me gustaría sb += "text"ser equivalente a sb.Append("text").

Aquí está la sintaxis para crear un método de extensión para StringBuilder:

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

Agrega con éxito el blahmétodo de extensión a StringBuilder.

Desafortunadamente, la sobrecarga del operador no parece funcionar:

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

Entre otras cuestiones, la palabra clave thisno está permitida en este contexto.

¿Son posibles las sobrecargas del operador mediante métodos de extensión? Si es así, ¿cuál es la forma correcta de hacerlo?

Jude Allred
fuente
44
Aunque esto al principio parece una buena idea, considere var otherSb = sb + "hola";
hacha - hecho con SOverflow

Respuestas:

150

Esto no es posible actualmente, porque los métodos de extensión deben estar en clases estáticas, y las clases estáticas no pueden tener sobrecargas del operador.

Mads Torgersen, C # Language PM dice:

... para el lanzamiento de Orcas, decidimos adoptar el enfoque cauteloso y agregar solo métodos de extensión regulares, en lugar de propiedades de extensión, eventos, operadores, métodos estáticos, etc. etc. Los métodos de extensión regulares eran lo que necesitábamos para LINQ, y tenían un diseño sintácticamente mínimo que no podría imitarse fácilmente para algunos de los otros tipos de miembros.

Somos cada vez más conscientes de que otros tipos de miembros de extensión podrían ser útiles, por lo que volveremos a este tema después de Orcas. Sin embargo, no hay garantías.

Editar:

Acabo de notar que Mads escribió más en el mismo artículo :

Lamento informar que no haremos esto en la próxima versión. Nos tomamos muy en serio a los miembros de la extensión en nuestros planes, y gastamos mucho esfuerzo tratando de hacerlos bien, pero al final no pudimos hacerlo lo suficientemente fácil y decidimos dar paso a otras características interesantes.

Esto todavía está en nuestro radar para futuras versiones. Lo que ayudará es si obtenemos una buena cantidad de escenarios convincentes que pueden ayudar a impulsar el diseño correcto.


Esta característica está actualmente en la tabla (potencialmente) para C # 8.0. Mads habla un poco más sobre implementarlo aquí .

Jacob Krall
fuente
Esta página ha sido retirada; Este problema aún no se ha resuelto.
Chris Moschini
17
Demasiado. Solo quería agregar un operador para multiplicar un TimeSpan por un valor escalar ... :(
Filip Skakun
Esperaba implementar este mismo concepto para lanzar Stringun PowerShell ScriptBlock.
Trevor Sullivan
¿Eso significa que no puedo sobrecargar% para ser xor entre booleanos? :( muerto en true.Xor(false)ese momento
SparK
3
@SparK ^es el operador xor en C #
Jacob Krall
57

Si controla los lugares donde desea utilizar este "operador de extensión" (que normalmente hace con los métodos de extensión de todos modos), puede hacer algo como esto:

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

} 

los StringBuilderWrapper clase declara un operador de conversión implícito de a StringBuilder y declara el +operador deseado . De esta forma, StringBuilderse puede pasar a ReceiveImportantMessage, que se convertirá silenciosamente en a StringBuilderWrapper, donde +se puede usar el operador.

Para que este hecho sea más transparente para las personas que llaman, puede declarar ReceiveImportantMessageque toma un StringBuildercódigo y simplemente usarlo de la siguiente manera:

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

O, para usarlo en línea donde ya está usando a StringBuilder, simplemente puede hacer esto:

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

yo creé una publicación sobre el uso de un enfoque similar para hacer IComparablemás comprensible.

Jordão
fuente
2
@Leon: Realmente quise componerlo, no heredarlo. De todos modos, no podría heredarlo ya que está sellado.
Jordão
55
@Leon: Ese es el corazón de esta técnica. Puedo hacer eso porque hay un operador de conversión implícito declarado StringBuilderWrapperque lo hace posible.
Jordão
1
@pylover: Tienes razón, esto requiere crear un nuevo tipo, que envolverá el StringBuildertipo y proporcionará un operador de conversión implícito. Después de eso, se puede usar con literales de cadena, como se demuestra en el ejemplo:sb += "Hello World!";
Jordão
2
¿Puedo sugerir entonces, agregar un método de extensión a String: PushIndent(" ".X(4))(también podría llamarse Times). O tal vez el uso de este constructor: PushIndent(new String(' ', 4)).
Jordão
1
@ Jordão: Gran respuesta;)
Vinicius
8

Parece que esto no es posible actualmente: hay un problema de comentarios abiertos que solicita esta característica en Microsoft Connect:

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

sugiriendo que podría aparecer en una versión futura, pero no está implementado para la versión actual.

Dylan Beattie
fuente
¿Qué quieres decir exactamente con "no es posible actualmente?" Debe ser posible en el CLR porque F # admite la extensión de todo.
Matthew Olenik
1
Creo que quiere decir que no es posible en C #, no en CLR. Todo lo relacionado con los métodos de extensión es un truco del compilador de C # de todos modos.
tofi9
1
Link está muerto ahora.
CBHacking
1

Aunque no es posible hacer los operadores, siempre puedes crear los métodos Agregar (o Concat), Restar y Comparar ...

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
fuente
1

Ja! Estaba buscando "sobrecarga del operador de extensión" con exactamente el mismo deseo, para sb + = (cosa).

Después de leer las respuestas aquí (y ver que la respuesta es "no"), para mis necesidades particulares, utilicé un método de extensión que combina sb.AppendLine y sb.AppendFormat, y se ve más ordenado que cualquiera de los dos.

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

}

Y entonces,

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

No es una respuesta general, pero puede ser de interés para los futuros buscadores que miran este tipo de cosas.

David Van Brink
fuente
44
Creo que su método de extensión se leería mejor si lo nombrara en AppendLinelugar de Line.
DavidRR
0

Es posible manipularlo con un envoltorio y extensiones, pero imposible hacerlo correctamente. Terminas con basura que anula totalmente el propósito. Tengo una publicación en algún lugar aquí que lo hace, pero no tiene valor.

Por cierto, todas las conversiones numéricas crean basura en el generador de cadenas que debe corregirse. Tuve que escribir un contenedor para lo que funciona y lo uso. Vale la pena leerlo.

motivará
fuente