184

我正在尝试使用扩展方法向 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

6 回答 6

158

目前这是不可能的,因为扩展方法必须在静态类中,而静态类不能有运算符重载。但该功能正在讨论 C# 的某些未来版本Mads在 2017 年的这段视频中谈到了更多关于实现它的内容。

C# 语言 PM 的 Mads Torgersen 说:

...对于 Orcas 版本,我们决定采取谨慎的方法,只添加常规扩展方法,而不是扩展属性、事件、运算符、静态方法等。常规扩展方法是 LINQ 所需要的,它们有一个语法上最小的设计,对于其他一些成员类型来说是不容易模仿的。

我们越来越意识到其他类型的扩展成员可能有用,因此我们将在 Orcas 之后回到这个问题。不过没有保证!

下面在同一篇文章中:

我很遗憾地报告我们不会在下一个版本中这样做。我们确实在我们的计划中非常重视扩展成员,并且花了很多精力试图让他们正确,但最终我们无法做到足够顺利,并决定让位于其他有趣的功能。

这仍然是我们对未来版本的关注。如果我们获得大量可以帮助推动正确设计的引人入胜的场景,将会有所帮助。

于 2008-10-05T21:04:11.473 回答
61

如果您控制要使用此“扩展运算符”的位置(无论如何您通常都使用扩展方法),您可以执行以下操作:

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

} 

该类从 aStringBuilderWrapper声明一个隐式转换运算符StringBuilder 声明所需的+运算符。这样,StringBuilder可以将 a 传递给ReceiveImportantMessage,它将被静默转换为 a StringBuilderWrapper+可以在其中使用运算符。

为了使这一事实对调用者更透明,您可以声明ReceiveImportantMessage为采用 aStringBuilder并使用如下代码:

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

或者,要在您已经使用 a 的地方内联使用它StringBuilder,您可以简单地执行以下操作:

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

我创建了一篇关于使用类似方法以使其IComparable更易于理解的帖子。

于 2010-02-03T19:08:09.693 回答
8

看来这目前是不可能的 - 有一个开放的反馈问题要求 Microsoft Connect 上的这个功能:

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

暗示它可能会出现在未来的版本中,但未在当前版本中实现。

于 2008-10-05T21:05:59.160 回答
2

哈!我正在以完全相同的愿望查找“扩展运算符重载”,对于sb += (thing).

在阅读了此处的答案(并看到答案为“否”)之后,出于我的特殊需要,我采用了一种扩展方法,它结合了sb.AppendLineand 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);

不是一个一般性的答案,但可能会引起未来寻求此类事物的人的兴趣。

于 2014-06-28T18:14:38.717 回答
1

虽然不可能使用运算符,但您始终可以创建 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;    
        }
    }


}
于 2012-05-17T21:18:32.930 回答
0

Its possible to rigg it with a wrapper and extensions but impossible to do it properly. You end with garbage which totally defeats the purpose. I have a post up somewhere on here that does it, but its worthless.

Btw All numeric conversions create garbage in string builder that needs to be fixed. I had to write a wrapper for that which does work and i use it. That is worth perusing.

于 2017-01-27T23:51:45.837 回答