簡體   English   中英

使用 C# 擴展方法重載運算符

[英]Operator Overloading with C# Extension Methods

我正在嘗試使用擴展方法將運算符重載添加到 C# StringBuilder class。具體來說,給定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

是否可以通過擴展方法添加運算符重載? 如果是這樣,請問 go 的正確方法是什么?

這目前是不可能的,因為擴展方法必須在靜態類中,而靜態類不能有運算符重載。 但是正在為 C# 的某些未來版本討論功能 Mads 在 2017 年的這段視頻中更多地談到了實施它。

C# 語言 PM 的 Mads Torgersen 說:

...對於 Orcas 版本,我們決定采取謹慎的方法,只添加常規擴展方法,而不是擴展屬性、事件、運算符、靜態方法等。常規擴展方法是我們 LINQ 所需要的,並且它們具有一種語法上最小的設計,對於其他一些成員類型無法輕易模仿。

我們越來越意識到其他類型的擴展成員可能有用,因此我們將在 Orcas 之后回到這個問題。 但是沒有保證!

在同一篇文章的下面進一步說明:

我很遺憾地報告,我們將不會在下一個版本中這樣做。 我們確實在我們的計划中非常重視擴展成員,並花了很多精力試圖讓他們正確,但最終我們無法讓它足夠順利,並決定讓位於其他有趣的功能。

這仍然在我們未來版本的雷達上。 如果我們獲得大量引人注目的場景來幫助推動正確的設計,那么將會有所幫助。

如果您控制要使用此“擴展運算符”的位置(無論如何您通常都會使用擴展方法),您可以執行以下操作:

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更易於理解的帖子

目前看來這是不可能的 - 有一個開放的反饋問題要求在 Microsoft Connect 上使用此功能:

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

暗示它可能會出現在未來的版本中,但未在當前版本中實現。

哈! 對於sb += (thing) ,我正在以完全相同的願望查找“擴展運算符重載”。

在閱讀了這里的答案(並且看到答案是“否”)之后,對於我的特殊需求,我使用了一個結合了sb.AppendLinesb.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);

不是一般性的答案,但可能對未來尋找此類事物的尋求者感興趣。

盡管不可能使用運算符,但您始終可以創建 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;    
        }
    }


}

可以用包裝器和擴展件來裝配它,但不可能正確地做到這一點。 你以完全違背目的的垃圾結束。 我在這里的某個地方有一個帖子可以做到這一點,但它毫無價值。

順便說一句,所有數字轉換都會在需要修復的字符串生成器中產生垃圾。 我不得不為那個工作的東西寫一個包裝器,我使用它。 那值得細讀。

以前的答案似乎仍然是正確的,這在 C# 中沒有實現。但是,您可以通過多一點代碼來獲得非常接近的結果。

選項1,StringBuilder周圍的包裝器class

像這樣:

public static class ExtStringBuilder
{
    public class WStringBuilder
    {
        private StringBuilder _sb { get; }
        public WStringBuilder(StringBuilder sb) => _sb = sb;
        public static implicit operator StringBuilder(WStringBuilder sbw) => sbw._sb;
        public static implicit operator WStringBuilder(StringBuilder sb) => new WStringBuilder(sb);
        public static WStringBuilder operator +(WStringBuilder sbw, string s) => sbw._sb.Append(s);
    }
    public static WStringBuilder wrap(this StringBuilder sb) => sb;
}

用法如下:

StringBuilder sb = new StringBuilder();
// requires a method call which is inconvenient
sb = sb.wrap() + "string1" + "string2";

要么:

WStringBuilder sbw = new StringBuilder();
sbw = sbw + "string1" + "string2";
// you can pass sbw as a StringBuilder parameter
methodAcceptsStringBuilder(sbw);
// but you cannot call methods on it
// this fails: sbw.Append("string3");

您可以以完全相同的方式為string而不是StringBuilder創建一個包裝器,這可能更合適,但在這種情況下,您可能會包裝許多string對象而不是 1 個StringBuilder object。


正如另一個答案所指出的那樣,這種策略似乎違背了擁有運營商的很多目的,因為根據用例,您使用它的方式會發生變化。 所以感覺有點尷尬。

在這種情況下,我同意,僅僅替換像StringBuilderAppend方法這樣的方法,開銷就太大了。 但是,如果您要在運算符后面放置更復雜的東西(例如需要 for 循環的東西),您可能會發現這對您的項目來說是一個方便的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM