Перевантаження оператора методами розширення C #


174

Я намагаюся використовувати методи розширення, щоб додати перевантаження оператора до класу 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в цьому контексті заборонено.

Чи можливо додати перевантаження оператора методами розширення? Якщо так, то який правильний шлях для цього?


4
Хоча це спочатку здається крутою ідеєю, розгляньте var otherSb = sb + "привіт";
хетч - зроблено з SOverflow

Відповіді:


150

Наразі це неможливо, оскільки методи розширення повинні бути у статичних класах, а статичні класи не можуть мати перевантаження операторів.

Мадс Торгерсен, Прем'єр-міністр C #, говорить:

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

Ми все більше усвідомлюємо, що інші види членів розширення можуть бути корисними, і тому ми повернемося до цього питання після Orcas. Ніяких гарантій, хоча!

Редагувати:

Я щойно помітив, Мадс написав більше в тій же статті :

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

Це все ще є на нашому радарі для майбутніх випусків. Що допоможе, якщо ми отримаємо велику кількість переконливих сценаріїв, які можуть допомогти привести правильний дизайн.


Ця функція наразі є на столі (можливо) для C # 8.0. Мадс розповідає трохи більше про його реалізацію тут .


Ця сторінка була знята; це питання досі не вирішено.
Кріс Москіні

17
Дуже погано. Я просто хотів додати оператора, щоб помножити TimeSpan на скалярне значення ... :(
Філіп Скакун

Я сподівався реалізувати цю саму концепцію для Stringпередачі PowerShell ScriptBlock.
Тревор Салліван

Це означає, що я не можу перевантажувати%, щоб стати xor між булевими? :( загинув true.Xor(false)тоді
SparK

3
@SparK ^- оператор xor у C #
Джейкоб Кралл

57

Якщо ви керуєте місцями, де ви хочете використовувати цього "оператора розширення" (що зазвичай ви робите з методами розширення), ви можете зробити щось подібне:

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, який буде мовчки перетворений в a 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більш зрозумілим.


2
@Leon: Я дійсно мав намір скласти його, а не успадковувати його. У всякому разі, я не міг успадкувати його, оскільки він запечатаний.
Йордао

5
@Leon: Це серце цієї техніки. Я можу це зробити, тому що існує неявний оператор перетворення, оголошений у цьому, StringBuilderWrapperщо робить це можливим.
Йордао,

1
@pylover: Ви маєте рацію, для цього потрібно створити новий тип, який оберне StringBuilderтип і забезпечить від нього неявний оператор перетворення. Після цього його можна використовувати з рядковими літералами, як показано на прикладі:sb += "Hello World!";
Jordão

2
Я можу запропонувати тоді додати метод розширення до String: PushIndent(" ".X(4))(можна також називати Times). Або , може бути , з допомогою цього конструктора: PushIndent(new String(' ', 4)).
Йордао

1
@ Jordão: Чудова відповідь;)
Вініцій

8

Здається, це наразі неможливо - у Microsoft Connect відкрита проблема із зворотним зв’язком, яка вимагає цієї функції:

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

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


Що саме ви маєте на увазі, "наразі це неможливо?" Це повинно бути можливо в CLR, оскільки F # підтримує розширення всього.
Матвій Оленік

1
Я думаю, що він означає, що це неможливо в C #, не в CLR. Вся річ із методами розширення - це трюк компілятора C #.
tofi9

1
Посилання зараз мертва.
CBHacking

1

Хоча це зробити операторам неможливо, ви завжди можете просто створити методи Додати (або Скласти), Відняти та Порівняти ....

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

Га! Я шукав "перевантаження оператора розширення" з точно таким же бажанням, для 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);

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


4
Я думаю, що ваш метод розширення буде читати краще, якби ви його назвали AppendLineзамість Line.
DavidRR

0

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

Btw Усі числові перетворення створюють сміття у програмі для створення рядків, яку потрібно виправити. Мені довелося написати обгортку для того, що працює, і я цим користуюся. Це варто проаналізувати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.