[英]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());
目前看來這是不可能的 - 有一個開放的反饋問題要求在 Microsoft Connect 上使用此功能:
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=168224
暗示它可能會出現在未來的版本中,但未在當前版本中實現。
哈! 對於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);
不是一般性的答案,但可能對未來尋找此類事物的尋求者感興趣。
盡管不可能使用運算符,但您始終可以創建 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# 中沒有實現。但是,您可以通過多一點代碼來獲得非常接近的結果。
像這樣:
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。
正如另一個答案所指出的那樣,這種策略似乎違背了擁有運營商的很多目的,因為根據用例,您使用它的方式會發生變化。 所以感覺有點尷尬。
在這種情況下,我同意,僅僅替換像StringBuilder
的Append
方法這樣的方法,開銷就太大了。 但是,如果您要在運算符后面放置更復雜的東西(例如需要 for 循環的東西),您可能會發現這對您的項目來說是一個方便的解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.