是否可以定义扩展运算符方法?

Posted

技术标签:

【中文标题】是否可以定义扩展运算符方法?【英文标题】:Is it possible define an extension operator method? 【发布时间】:2011-07-27 23:16:35 【问题描述】:

是否可以定义一个同时是操作符的扩展方法? 我希望为一个固定类添加使用实际上无法应用的已知运算符的可能性。 对于这种特殊情况,我想这样做:

   somestring++;  //i really know that this string contains a numeric value

而且我不想传播所有代码的类型转换。 我知道我可以在字符串上创建包装类并定义该运算符,但我想知道这种事情是否可以避免使用 MySpecialString 搜索和替换每个字符串声明。

编辑:正如大多数人所说的字符串是密封的,所以推导是不可能的,所以我将“派生”修改为“包装器”,我的错误。

【问题讨论】:

见this 回答:“目前不可能,因为扩展方法必须在静态类中,而静态类不能有运算符重载。” :( 我明白了,但正如 2008 年写的那样,我希望事情已经改变了 【参考方案1】:

这在 C# 中是不可能的,但为什么不是标准的扩展方法呢?

 public static class StringExtensions 
     public static string Increment(this string s) 
          ....
     
 

我认为somestring.Increment() 更具可读性,因为您不会让那些真的不希望看到++ 应用于字符串的人感到困惑。

【讨论】:

【参考方案2】:

一个明确的例子就是能够扩展 TimeSpan 类以包含 * 和 / 运算符。

这是理想的工作方式......

public static class TimeSpanHelper

    public static TimeSpan operator *(TimeSpan span, double factor)
    
        return TimeSpan.FromMilliseconds(span.TotalMilliseconds * factor);
    

    public static TimeSpan operator *(double factor, TimeSpan span)  // * is commutative
    
        return TimeSpan.FromMilliseconds(span.TotalMilliseconds * factor);
    

    public static TimeSpan operator /(TimeSpan span, double sections)
    
        return TimeSpan.FromMilliseconds(span.TotalMilliseconds / factor);
    

    public static double operator /(TimeSpan span, TimeSpan period)
    
        return span.TotalMilliseconds / period.TotalMilliseconds);
    


【讨论】:

确实如此。使用大量 .Multiply() .Divide() 等进行时间跨度数学很快就会变得丑陋 我尝试并得到“静态类不能包含用户定义的运算符”,如果它不是静态的,那么我得到“二元运算符重载的参数之一应该是包含类型” 这正是我想要做的。【参考方案3】:

不,不能在课堂外进行。 ++ 运算符应在递增的类中定义。您可以创建自己的类,该类可以从字符串转换并具有++ 重载,或者您可以忘记这个想法并使用常规方法。

【讨论】:

【参考方案4】:

不,您不能拥有同时也是运算符的扩展方法。扩展方法只能在静态类中声明,不能有实例,根据 C# 规范,

用户定义的运算符声明始终要求至少一个参数是包含运算符声明的类或结构类型。 [7.3.2]

因此,扩展方法不可能同时是重载运算符。

此外,您不能覆盖 System.String,因为它是 sealed 类。

【讨论】:

【参考方案5】:

字符串类在 C# 中是密封的,因此实际上创建字符串派生类是不可能的

话虽如此,扩展方法当然可以正常工作(就像帮助类中的标准静态方法一样),但它不会是运算符,只是通常命名的方法。

【讨论】:

【参考方案6】:

目前不支持,因为扩展方法是在单独的静态类中定义的,静态类不能有运算符重载定义。

【讨论】:

【参考方案7】:

这都是真的,但是 M$ 将来添加这个功能会很好。有时框架只是缺少一些东西,而扩展可以帮助填补空白(或解决问题),这有时可能是运营商。

一个例子。要比较 IP 地址,您必须使用 Equals 方法直接比较(当然,也可以比较结构的某些部分,就像单独比较地址字节一样 - 但这是另一回事)。但是,使用 == 运算符总是在对象级别返回 false(即不将它们转换为字符串等)。将 Equals 方法调用放在 == 运算符调用中有多难(这是修辞),但我们做不到。这是不一致的,并且是错误潜入的地方(注意它不会失败,只是总是等于 false - 而 Equals 不会)。

【讨论】:

【参考方案8】:

我认为您应该使用包装类,即使您可以编写扩展运算符。

//i really know that this string contains a numeric value

正是为这种情况发明了类型安全。

另一种看待它的方式是,通过编写该运算符,您破坏了许多其他与string 类一起使用的函数和运算符,因为它们不一定保留包含数值的属性。通过使用包装类而不是派生类,您只需重新实现string 的那些对数字字符串有意义的功能。

【讨论】:

【参考方案9】:

我的情况与您描述的非常相似:我需要增加 Windows 窗体文本框中的文本(肯定包含数值)。

正如您所描述的,我了解您的需求

一些字符串++; //我真的知道这个字符串包含一个数值

我的灵魂是我认为与您的描述接近的东西

somestring = (incrementable)somestring + 1

我需要做的就是

    创建名为incrementable的类 在其中定义一个显式运算符(以帮助将string 转换为incrementable) 在其中定义一个隐式运算符(以帮助将incrementable 转换回string+ 的运算符(加号)

这是我的课程的完整外观

public class incrementable

    public string s; // For storing string value that holds the number

    public incrementable(string _s)
    
        s = _s;
    

    public static explicit operator incrementable(string tmp)
    
        return new incrementable(tmp);
    

    public static implicit operator string(incrementable tmp)
    
        return tmp.s;
    

    public static incrementable operator +(incrementable str, int inc) // This will work flawlessly like `somestring = (incrementable)somestring + 1`
        => new incrementable((Convert.ToInt32(str.s) + inc).ToString());

    public static incrementable operator ++(incrementable str) // Unfortunately won't work, see below
        => new incrementable((Convert.ToInt32(str.s) + 1).ToString());

不幸的是,我无法通过使用一元 ++ 运算符来改进我的课程。反对使用像 ((incrementable)somestring)++ 这样的隐式转换的原因是它会导致错误说 The operand of an increment or decrement operator must be a variable, property or indexer 因此不能是该转换的结果。

无论如何,希望这会有所帮助!

【讨论】:

除了这个答案之外,还可以在此处找到可重载和不可重载运算符的列表:tutorialspoint.com/csharp/csharp_operator_overloading.htm【参考方案10】:

如其他答案所示,不能直接完成。但是如果你需要的话,比如说你想改进 StringBuilder 喜欢

void Main()

    var log = (StringBuilder)"Hello ";
    log += "World!";
    log += "\nThis example shows how to extend StringBuilder";
    log.ToString().Dump();

您如何实现这一点(即使用+ 运算符而不是sb.Append(str);)?


答案: 在这种情况下,你不能直接做,但你可以做的是:

Run it in DotNetFiddle

void Main()

    var log = (StrBuilder)"Hello "; // same as: "Hello ".ToStrBuilder();
    log += "World!";
    log += "\nThis example shows how to extend StringBuilder";
    log.ToString().Dump();


public static class Extensions

    public static StrBuilder ToStrBuilder(this string str)
    
        return new StrBuilder(str);
       


public class StrBuilder

    private StringBuilder sb;

    public StrBuilder()
    
        sb = new StringBuilder();
    

    public StrBuilder(string strB)
    
        sb = new StringBuilder(strB);
    

    public static implicit operator StrBuilder(string self)
    
        return new StrBuilder(self);
    

    public static StrBuilder operator +(StrBuilder sbA, string strB)
           
        return sbA.Append(strB);
    

    public StrBuilder Append(string strB)
    
        sb.Append(strB);
        return this;
    

    public override string ToString()
    
        return sb.ToString();
    

注意:你不能从 StringBuilder 继承,因为它是一个密封的类,但是你可以写一个“装箱”一个 StringBuilder 的类——也就是这里做了什么(感谢@ 987654322@关于隐式转换)。

【讨论】:

以上是关于是否可以定义扩展运算符方法?的主要内容,如果未能解决你的问题,请参考以下文章

扩展函数和运算符重载

使用 map 时使用扩展运算符扩展数组

无法识别扩展运算符的重载方法

Javascript 扩展运算符可以包含对象的未定义字段吗?

VueJS:是不是可以将扩展运算符用于计算属性?

PhpStorm 中的扩展运算符