简体   繁体   English

使用 C# 扩展方法重载运算符

[英]Operator Overloading with C# Extension Methods

I'm attempting to use extension methods to add an operater overload to the C# StringBuilder class. Specifically, given StringBuilder sb , I'd like sb += "text" to become equivalent to sb.Append("text") .我正在尝试使用扩展方法将运算符重载添加到 C# StringBuilder class。具体来说,给定StringBuilder sb ,我希望sb += "text"等效于sb.Append("text")

Here's the syntax for creating an extension method for StringBuilder :下面是为StringBuilder创建扩展方法的语法:

public static class sbExtensions
{
    public static StringBuilder blah(this StringBuilder sb)
    {
        return sb;
    }
} 

It successfully adds the blah extension method to the StringBuilder .它成功地将blah扩展方法添加到StringBuilder

Unfortunately, operator overloading does not seem to work:不幸的是,运算符重载似乎不起作用:

public static class sbExtensions
{
    public static StringBuilder operator +(this StringBuilder sb, string s)
    {
        return sb.Append(s);
    }
} 

Among other issues, the keyword this is not allowed in this context.除其他问题外,此上下文中不允许使用关键字this

Are adding operator overloads via extension methods possible?是否可以通过扩展方法添加运算符重载? If so, what's the proper way to go about it?如果是这样,请问 go 的正确方法是什么?

This is not currently possible, because extension methods must be in static classes, and static classes can't have operator overloads.这目前是不可能的,因为扩展方法必须在静态类中,而静态类不能有运算符重载。 But the feature is being discussed for some future release of C# .但是正在为 C# 的某些未来版本讨论功能 Mads talked a bit more about implementing it in this video from 2017 . Mads 在 2017 年的这段视频中更多地谈到了实施它。

On why it isn't currently implemented, Mads Torgersen, C# Language PM says: C# 语言 PM 的 Mads Torgersen 说:

...for the Orcas release we decided to take the cautious approach and add only regular extension methods, as opposed to extention properties, events, operators, static methods, etc etc. Regular extension methods were what we needed for LINQ, and they had a syntactically minimal design that could not be easily mimicked for some of the other member kinds. ...对于 Orcas 版本,我们决定采取谨慎的方法,只添加常规扩展方法,而不是扩展属性、事件、运算符、静态方法等。常规扩展方法是我们 LINQ 所需要的,并且它们具有一种语法上最小的设计,对于其他一些成员类型无法轻易模仿。

We are becoming increasingly aware that other kinds of extension members could be useful, and so we will return to this issue after Orcas.我们越来越意识到其他类型的扩展成员可能有用,因此我们将在 Orcas 之后回到这个问题。 No guarantees, though!但是没有保证!

Further below in the same article:在同一篇文章的下面进一步说明:

I am sorry to report that we will not be doing this in the next release.我很遗憾地报告,我们将不会在下一个版本中这样做。 We did take extension members very seriously in our plans, and spent a lot of effort trying to get them right, but in the end we couldn't get it smooth enough, and decided to give way to other interesting features.我们确实在我们的计划中非常重视扩展成员,并花了很多精力试图让他们正确,但最终我们无法让它足够顺利,并决定让位于其他有趣的功能。

This is still on our radar for future releases.这仍然在我们未来版本的雷达上。 What will help is if we get a good amount of compelling scenarios that can help drive the right design.如果我们获得大量引人注目的场景来帮助推动正确的设计,那么将会有所帮助。

If you control the places where you want to use this "extension operator" (which you normally do with extension methods anyway), you can do something like this:如果您控制要使用此“扩展运算符”的位置(无论如何您通常都会使用扩展方法),您可以执行以下操作:

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

} 

The StringBuilderWrapper class declares an implicit conversion operator from a StringBuilder and declares the desired + operator.所述StringBuilderWrapper类声明的隐式转换运算符从一个StringBuilder ,并声明所需的+运算符。 This way, a StringBuilder can be passed to ReceiveImportantMessage , which will be silently converted to a StringBuilderWrapper , where the + operator can be used.这样,可以将StringBuilder传递给ReceiveImportantMessage ,后者将被静默转换为StringBuilderWrapper ,其中可以使用+运算符。

To make this fact more transparent to callers, you can declare ReceiveImportantMessage as taking a StringBuilder and just use code like this:为了使这个事实对调用者更透明,您可以将ReceiveImportantMessage声明为采用StringBuilder并使用如下代码:

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

Or, to use it inline where you're already using a StringBuilder , you can simply do this:或者,要在您已经使用StringBuilder地方使用它,您可以简单地执行以下操作:

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

I created a post about using a similar approach to make IComparable more understandable.我创建了一篇关于使用类似方法使IComparable更易于理解的帖子

It appears this isn't currently possible - there's an open feedback issue requesting this very feature on Microsoft Connect:目前看来这是不可能的 - 有一个开放的反馈问题要求在 Microsoft Connect 上使用此功能:

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

suggesting it might appear in a future release but isn't implemented for the current version.暗示它可能会出现在未来的版本中,但未在当前版本中实现。

Hah!哈! I was looking up "extension operator overloading" with exactly the same desire, for sb += (thing) .对于sb += (thing) ,我正在以完全相同的愿望查找“扩展运算符重载”。

After reading the answers here (and seeing that the answer is "no"), for my particular needs, I went with an extension method which combines sb.AppendLine and sb.AppendFormat , and looks tidier than either.在阅读了这里的答案(并且看到答案是“否”)之后,对于我的特殊需求,我使用了一个结合了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);
    }
}

And so,所以,

sb.Line("the first thing is {0}", first);
sb.Line("the second thing is {0}", second);

Not a general answer, but may be of interest to future seekers looking at this kind of thing.不是一般性的答案,但可能对未来寻找此类事物的寻求者感兴趣。

Though it's not possible to do the operators, you could always just create Add (or Concat), Subtract, and Compare methods....尽管不可能使用运算符,但您始终可以创建 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;    
        }
    }


}

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.那值得细读。

The previous answers still seem to be correct, this is not implemented in C#. You can, however, get very close with a little more code.以前的答案似乎仍然是正确的,这在 C# 中没有实现。但是,您可以通过多一点代码来获得非常接近的结果。

Option 1, wrapper class around StringBuilder选项1,StringBuilder周围的包装器class

Something like this:像这样:

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

usage like this:用法如下:

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

Or:要么:

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");

You can make a wrapper for string instead of StringBuilder in the exact same fashion, which might be a better fit, but in this case, you'll be wrapping possibly many string objects instead of 1 StringBuilder object.您可以以完全相同的方式为string而不是StringBuilder创建一个包装器,这可能更合适,但在这种情况下,您可能会包装许多string对象而不是 1 个StringBuilder object。


As another answer points out , this strategy seems to defeat a lot of the purpose of having an operator since depending on the use case, the way you use it changes.正如另一个答案所指出的那样,这种策略似乎违背了拥有运营商的很多目的,因为根据用例,您使用它的方式会发生变化。 So it feels a little awkward.所以感觉有点尴尬。

In this case, I agree, this is far too much overhead just to replace a method like StringBuilder 's Append method.在这种情况下,我同意,仅仅替换像StringBuilderAppend方法这样的方法,开销就太大了。 However, if you are putting something more complicated behind the operator (something requiring a for loop, for instance), you may find this is a convenient solution for your project.但是,如果您要在运算符后面放置更复杂的东西(例如需要 for 循环的东西),您可能会发现这对您的项目来说是一个方便的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM