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

Я пытаюсь использовать методы расширения, чтобы добавить перегрузку оператора в класс C # 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 недопустимо в этом контексте.

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


person Jude Allred    schedule 05.10.2008    source источник
comment
Хотя поначалу это кажется классной идеей, рассмотрим var otherSb = sb + hi;   -  person hatchet - done with SOverflow    schedule 08.06.2012


Ответы (6)


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

Мэдс Торгерсен, менеджер по языку C #, говорит о том, почему это в настоящее время не реализовано:

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

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

Далее в той же статье:

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

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

person Jacob Krall    schedule 05.10.2008
comment
С тех пор эта страница была удалена; этот вопрос до сих пор не решен. - person Chris Moschini; 28.12.2012
comment
Очень жаль. Я просто хотел добавить оператор для умножения TimeSpan на скалярное значение ... :( - person Filip Skakun; 11.01.2013
comment
Я надеялся реализовать ту же концепцию, чтобы преобразовать String в PowerShell ScriptBlock. - person Trevor Sullivan; 17.02.2014
comment
Это означает, что я не могу перегрузить%, чтобы быть xor между логическими значениями? :( умер в true.Xor(false) тогда - person SparK; 31.05.2016
comment
@SparK ^ - оператор xor в C # - person Jacob Krall; 01.06.2016
comment
@JacobKrall Хммм, я думал, что это только побитовый, а не логический оператор. Спасибо! - person SparK; 01.06.2016
comment
Это все еще рассматривается? Кажется, что перегрузить операторы с помощью методов расширения все еще невозможно. - person SuperJMN; 04.08.2020
comment
@SuperJMN да, все еще в стадии рассмотрения, но не для C # 8.0. Это не вошло в разрез - person Neuron; 10.09.2020
comment
Вероятно, не в C # 9 ... docs.microsoft .com / en-us / dotnet / csharp / whats-new / csharp-9. - person Elenesski; 17.01.2021

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

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 больше понятно.

person Jordão    schedule 03.02.2010
comment
Я думаю, вы имели в виду, что StringBuilderWrapper должен наследовать StringBuilder? - person Leon van der Walt; 23.08.2010
comment
@Leon: Я действительно хотел составить его, а не унаследовать от него. В любом случае, я не мог унаследовать его, поскольку он запечатан. - person Jordão; 23.08.2010
comment
честно, но вы передаете StringBuilder в ReceiveImportantMessage, который ожидает StringBuilderWrapper - person Leon van der Walt; 24.08.2010
comment
@Leon: Это сердце этой техники. Я могу это сделать, потому что в StringBuilderWrapper объявлен оператор неявного преобразования, делает это возможным. - person Jordão; 24.08.2010
comment
Это необходимо для создания нового типа и не может использоваться с литералами вроде. - person pylover; 20.07.2012
comment
@pylover: Вы правы, для этого необходимо создать новый тип, который будет обертывать тип StringBuilder и предоставлять из него оператор неявного преобразования. После этого его можно использовать со строковыми литералами, как показано в примере: sb += "Hello World!"; - person Jordão; 20.07.2012
comment
@ Жордао: Да, но я хочу перегрузить *operator для строкового литерала и использовать его в шаблонах T4, таких как PushIndent(" " * 4), точно так же, как python. - person pylover; 20.07.2012
comment
Тогда позвольте мне предложить добавить метод расширения к String: PushIndent(" ".X(4)) (также может называться Times). Или, может быть, с помощью этого конструктора: PushIndent(new String(' ', 4)). - person Jordão; 20.07.2012
comment
@ Жордао: Отличный ответ;) - person Vinicius; 09.12.2016
comment
Отличный ответ, ты !! - person bjnr; 07.07.2017

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

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

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

person Dylan Beattie    schedule 05.10.2008
comment
Что именно вы имеете в виду, в настоящее время невозможно? Это должно быть возможно в среде CLR, потому что F # поддерживает все расширения. - person Matthew Olenik; 03.02.2010
comment
Я думаю, он имеет в виду, что это невозможно в C #, а не в CLR. В любом случае, вся эта штука с методами расширения - это уловка компилятора C #. - person tofi9; 31.05.2010
comment
Линк мертв. - person CBHacking; 03.08.2017

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

Прочитав здесь ответы (и увидев, что ответ отрицательный), для моих конкретных нужд я выбрал метод расширения, который сочетает в себе 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);

Не общий ответ, но может быть интересен будущим ищущим, ищущим подобные вещи.

person david van brink    schedule 28.06.2014
comment
Я думаю, ваш метод расширения был бы лучше, если бы вы назвали его AppendLine вместо Line. - person DavidRR; 29.09.2015

Хотя использовать операторы невозможно, вы всегда можете просто создать методы 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;    
        }
    }


}
person Chuck Rostance    schedule 17.05.2012

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

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

person will motil    schedule 27.01.2017