使用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 +“ hi”;
柴刀-SOverflow 2012年

Answers:


150

目前这是不可能的,因为扩展方法必须在静态类中,并且静态类不能有运算符重载。

C#语言PM的Mads Torgersen说:

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

我们越来越意识到其他类型的扩展成员也可能有用,因此我们将在Orcas之后再讨论这个问题。但是,没有任何保证!

编辑:

我刚刚注意到,Mads在同一篇文章中写道:

遗憾的是,我们将在下一版本中不再这样做。我们确实在计划中非常重视扩展成员,并花了很多努力来使他们正确,但是最终我们无法使其顺畅,因此决定让位给其他有趣的功能。

对于将来的版本,这仍然是我们的注意事项。如果我们能获得大量引人入胜的方案来帮助进行正确的设计,那将会有所帮助。


当前(可能)在C#8.0上具有此功能。Mads 在这里讨论了更多有关实现它的内容。


此页面已被删除;这个问题仍然没有解决。
克里斯·莫斯基尼

17
太糟糕了。我只是想添加一个运算符,将TimeSpan与标量值相乘... :(
Filip Skakun 2013年

我希望实现相同的概念以将Stringa强制转换为PowerShell ScriptBlock
Trevor Sullivan 2014年

这意味着我不能超载%成为布尔值的xor?:( true.Xor(false)当时死亡
SparK

3
@SparK ^是C#中的xor运算符
Jacob Krall

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可以将a传递给ReceiveImportantMessage,然后将其静默转换为StringBuilderWrapper+可以在其中使用运算符。

为了使这一事实对调用者更加透明,您可以声明ReceiveImportantMessage为a 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:我真的是想组成它,而不是继承它。无论如何,由于它是密封的,所以我无法继承它。
Jordão酒店

5
@Leon:这就是这项技术的核心。我可以这样做是因为其中声明了一个隐式转换运算符StringBuilderWrapper从而使它成为可能。
Jordão酒店

1
@pylover:是的,这需要创建一个新类型,该新类型将包装该StringBuilder类型并从中提供隐式转换运算符。在此之后,它可以:与字符串文字使用,这表现在示例sb += "Hello World!";
Jordão酒店

2
然后,我可以建议向String:添加扩展方法PushIndent(" ".X(4))(也可以称为Times)。或使用此构造函数:PushIndent(new String(' ', 4))
Jordão酒店

1
@Jordão:好的答案;)
Vinicius

8

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

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

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


您究竟是什么意思,“目前尚不可能?” 由于C#支持扩展所有内容,因此在CLR中必须有可能。
马修·奥莱尼克

1
我认为他的意思是在C#中而不是CLR中是不可能的。无论如何,整个扩展方法都是C#编译器的把戏。
tofi9年

1
链接已死。
CBHacking

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


}

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

可以使用包装器和扩展件来固定它,但无法正确地进行操作。您以垃圾彻底结束,这完全达不到目的。我在这里某处张贴了一篇文章,但它毫无价值。

顺便说一下,所有数字转换都会在字符串生成器中创建垃圾,需要对其进行修复。我必须写一个包装的工作,我用它。值得细读。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.