[英]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.