Перегрузка оператора с помощью методов расширения С#

Я пытаюсь использовать методы расширения для добавления перегрузки оператора в класс С# StringBuilder. В частности, данный StringBuilder sb, я хотел бы, чтобы sb += "text" стал эквивалентным sb.Append("text").

Вот синтаксис для создания метода расширения для StringBuilder:

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

Он успешно добавляет метод расширения blah к StringBuilder.

К сожалению, перегрузка оператора не работает:

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

Среди других проблем ключевое слово this не допускается в этом контексте.

Возможно ли добавление перегрузок операторов через методы расширения? Если да, то каков правильный способ сделать это?

Ответ 1

В настоящее время это невозможно, поскольку методы расширения должны быть в статических классах, а статические классы не могут иметь перегрузки оператора.

Mads Torgersen, С# Language PM говорит:

... для выпуска Orcas мы решили принять осторожный подход и добавить только регулярные методы расширения, так как противоположные свойствам расширения, события, операторы, статические методы и т.д. и т.д. Регулярные методы расширения что нам нужно для LINQ, и они синтаксически минимальный дизайн, который нельзя было легко подражать других видов участников.

Мы все больше осознаем что другие виды членов расширения может быть полезным, и поэтому мы вернемся к этому вопросу после Орки. нет гарантии, хотя!

Edit:

Я только что заметил, Mads написал больше в той же статье:

Мне жаль сообщить, что мы не будем сделайте это в следующем выпуске. Мы действительно принимали серьезно в наших планах, и провел много усилий, пытаясь получить их право, но в итоге мы не смогли получить он достаточно гладко и решил дать путь к другим интересным особенностям.

Это все еще на нашем радаре для будущего релизы. Что поможет, если мы получим достаточное количество убедительных сценариев что может помочь управлять правильным дизайном.


Эта функция в настоящее время находится в таблице (потенциально) для С# 8.0. Mads говорит немного больше о ее реализации здесь.

Ответ 2

Если вы управляете местами, где вы хотите использовать этот "оператор расширения" (который вы обычно делаете с методами расширения), вы можете сделать что-то вроде этого:

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

} 

Класс StringBuilderWrapper объявляет неявный оператор преобразования из StringBuilder и объявляет желаемый оператор +. Таким образом, StringBuilder можно передать в ReceiveImportantMessage, который будет тихо преобразован в StringBuilderWrapper, где может использоваться оператор +.

Чтобы сделать этот факт более прозрачным для вызывающих, вы можете объявить ReceiveImportantMessage как принимающий StringBuilder и просто использовать такой код:

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

Или, чтобы использовать его в строке, где вы уже используете StringBuilder, вы можете просто сделать это:

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

Я создал сообщение об использовании подобного подхода, чтобы сделать IComparable более понятным.

Ответ 3

Кажется, что в настоящее время это невозможно - существует проблема с обратной связью, запрашивающая эту функцию в Microsoft Connect:

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

предполагая, что он может появиться в будущей версии, но не реализован для текущей версии.

Ответ 4

Хотя это невозможно сделать операторам, вы всегда можете просто создавать методы Add (или Concat), Subtract и Compare....

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


}

Ответ 5

Хах! Я искал "перегрузку оператора расширения" с точно таким же желанием, для sb + = (вещь).

Прочитав ответы здесь (и увидев, что ответ "нет" ), для моих конкретных потребностей я пошел с расширением, который объединяет sb.AppendLine и sb.AppendFormat и выглядит более аккуратным, чем любой.

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

}

Итак,

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

Не общий ответ, но может представлять интерес для будущих искателей, рассматривающих такие вещи.

Ответ 6

Возможно его оснастить оболочкой и расширениями, но сделать это невозможно. Вы заканчиваете мусором, который полностью побеждает цель. У меня есть почта где-то здесь, что делает это, но его ничего не стоит.

Btw Все числовые преобразования создают мусор в построителе строк, который необходимо исправлять. Мне пришлось написать оболочку для того, что работает, и я использую ее. Это заслуживает внимания.