【问题标题】:Operator Overloading with C# Extension Methods使用 C# 扩展方法的运算符重载
【发布时间】:2010-09-15 10:02:44
【问题描述】:

我正在尝试使用扩展方法向 C# StringBuilder 类添加运算符重载。具体来说,给定StringBuildersb,我希望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

是否可以通过扩展方法添加运算符重载?如果是这样,正确的方法是什么?

【问题讨论】:

  • 虽然乍一看这似乎是一个很酷的想法,但请考虑 var otherSb = sb + "hi";

标签: c# extension-methods operator-overloading


【解决方案1】:

目前这是不可能的,因为扩展方法必须在静态类中,而静态类不能有运算符重载。但是feature is being discussed for some future release of C#。 Mads 谈到了更多关于实现它in this video from 2017

关于目前未实施的原因,C# 语言 PM 的 Mads Torgersen 说:

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

我们越来越意识到 其他种类的扩展成员 可能有用,所以我们将返回 虎鲸之后就到了这个问题。不 不过保证!

在同一篇文章中进一步说明:

很遗憾地报告我们不会 在下一个版本中执行此操作。我们 确实非常重视扩展成员 认真地在我们的计划中,并花费了 很多努力试图让他们 是的,但最后我们无法得到 它足够顺利,并决定给 其他有趣的功能。

这仍然是我们未来的雷达 发布。有什么帮助是如果我们得到 大量引人注目的场景 这有助于推动正确的设计。

【讨论】:

  • 此页面已被撤下;此问题仍未解决。
  • 太糟糕了。我只是想添加一个运算符来将 TimeSpan 乘以标量值... :(
  • 我希望实现同样的概念,将 String 转换为 PowerShell ScriptBlock
  • @SparK ^ 是 C# 中的异或运算符
  • @SuperJMN 是的,仍在考虑中,但不适用于 c# 8.0。它没有成功
【解决方案2】:

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

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 声明一个implicit conversion operator 并且 声明所需的+ 运算符。这样,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());

我创建了a post,使用类似的方法使IComparable 更易于理解。

【讨论】:

  • @Leon:我真的是想创作它,而不是继承它。无论如何,我不能继承它,因为它是密封的。
  • @Leon:这是这项技术的核心。我可以这样做,因为在 StringBuilderWrapper 中声明了一个 implicit conversion operator,这使得它成为可能。
  • @pylover:你说得对,这需要创建一个新类型,它将包装StringBuilder 类型并从中提供一个隐式转换运算符。之后,它可以与字符串文字一起使用,如示例所示:sb += "Hello World!";
  • 那么我建议给String添加一个扩展方法:PushIndent(" ".X(4))(也可以称为Times)。或者也许使用这个构造函数:PushIndent(new String(' ', 4)).
  • @Jordão:很好的答案;)
【解决方案3】:

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

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

建议它可能会出现在未来的版本中,但不会在当前版本中实现。

【讨论】:

  • 你到底是什么意思,“目前不可能?”在 CLR 中一定是可能的,因为 F# 支持扩展一切。
  • 我认为他的意思是在 C# 中是不可能的,而不是在 CLR 中。无论如何,整个扩展方法都是一个 C# 编译器技巧。
  • 链接已失效。
【解决方案4】:

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

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

所以,

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

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

【讨论】:

  • 我认为如果您将扩展方法命名为 AppendLine 而不是 Line,它会更好读。
【解决方案5】:

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


}

【讨论】:

    【解决方案6】:

    可以使用包装器和扩展来装配它,但无法正确完成。你以完全违背目的的垃圾结束。 我在这里某处发了一个帖子,可以做到这一点,但它毫无价值。

    顺便说一句 所有数字转换都会在需要修复的字符串生成器中创建垃圾。我必须为确实有效的包装器编写一个包装器并使用它。值得细读。

    【讨论】:

      猜你喜欢
      • 2020-08-25
      • 2010-10-15
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 2010-12-20
      • 2014-10-27
      • 2019-06-11
      • 1970-01-01
      相关资源
      最近更新 更多