【问题标题】:C# String Operator OverloadingC# 字符串运算符重载
【发布时间】:2010-04-06 19:20:11
【问题描述】:

G'Day Mates -

重载字符串运算符、=的正确方法是什么(不包括是否可取的论点)?

我已经尝试了五种方法到星期天,我得到了各种错误 - 我最好的方法是声明一个部分类并从那里重载,但由于某种原因它不会工作。

namespace System
{
   public partial class String
   {
       public static Boolean operator <(String a, String b)
       {
           return a.CompareTo(b) < 0;
       }

       public static Boolean operator >(String a, String b)
       {
           return a.CompareTo(b) > 0;
       }
   }

}

【问题讨论】:

  • 仅供参考:部分类需要驻留在同一个程序集(项目)中。
  • 那些操作符已经为字符串定义了,所以重新定义它们是没有意义的。如果可能的话,它可能会破坏很多代码。
  • 未定义这些运算符。至少对于 c#。它们存在于 VB 中。也许这就是OP需要它们的原因。这就是我来这里的原因。

标签: c# operator-overloading


【解决方案1】:

String 是一个密封类。你不能从它继承,并且没有 String 的原始源,你不能编译它的部分类。即使您掌握了源代码(可以通过 Reflector 或通过 Visual Studio 符号下载),您仍然会遇到问题,因为它是运行时的一等公民。

你真的需要 作为字符串的运算符吗?如果是这样...为什么不直接使用extension methods

public static bool IsLessThan(this string a, string b) 
{ 
    return a.CompareTo(b) < 0; 
} 

public static bool IsGreaterThan(this string a, string b) 
{ 
    return a.CompareTo(b) > 0; 
}


// elsewhere...
foo.IsLessThan(bar); // equivalent to foo < bar

【讨论】:

  • 即使他可以从它继承,也只有属于他的继承类型并被引用为继承类型的实例才能使用备用运算符。因为运算符不是多态的(它们被重载,而不是被覆盖),即使将子类型引用为 string 也会消除备用运算符功能。
  • @Adam Robinson:完全正确。底线:@ScottSEA 根本无法做他想做的事。
  • 我知道您的帖子与扩展方法无关,但提供链接可能会很有用。对于 C# 的新手来说,它看起来被称为错误的。 msdn.microsoft.com/en-ca/library/vstudio/bb383977.aspx
【解决方案2】:

没有办法将编译器的任何内置行为替换为您自己的。您不能覆盖现有的用于比较、转换、算术等的内置运算符。这是设计使然;这样有人可以阅读您的代码并知道int x = M(); int y = x + 2; 进行整数运算,而不是格式化您的硬盘。

您能解释一下为什么要这样做吗?也许有更好的方法来做你想做的事。

【讨论】:

  • 感谢您的回答。作为一个坚定不移的设计决策,它可以解释为什么自定义字符串运算符在十年前是不可能的,并且直到今天仍然是不可能的。
  • 我要寻找的是将String.Concat(Enumerable.Repeat("Hello", 4)) 表示为"Hello" * 4 有人知道这是否可能吗?
  • @AlexeyKhoroshikh:不。没有可以定义该运算符的类型。
【解决方案3】:

简单的答案是你不能;无法修改另一个类的运算符。部分类只允许用于在所有文件中声明partial在同一程序集中定义的类。

【讨论】:

    【解决方案4】:

    您是指System.String 类吗?这在 C# 中是不可能的。您不能将扩展运算符添加到现有类。这是一个非常需要的功能。

    【讨论】:

      【解决方案5】:
      • 您不能为字符串创建分部类,因为字符串类本身不是分部,因此无法与您的分部类一起使用。

      • 字符串是密封的,所以你可以继承它然后重载操作符。

      简而言之,唉,你不能做你想做的事。

      不知道你到底想做什么,我不能推荐一个好的选择。但是,请查看 extension methods,这通常适用于各种情况。抛开您是否应该 :),您可以向字符串类添加一个名为IsGreaterThan 的方法,并根据您的意愿返回 true 或 false。这很好,因为您可以给扩展方法起一个名称,使其含义清晰,保持现有运算符的完整性(无论如何您都别无选择),并允许快速/简单的代码。

      【讨论】:

        【解决方案6】:

        不能直接重载 >= 和 和 == 达到相同的效果。

        您的代码对我来说似乎是正确的,除了您错过了 == 的重载。

        似乎我错了,但是,您总是可以退回到反思。我认为如果您进行一些挖掘和黑客攻击,您可以使用反射来扩展类,因为反射允许在运行时添加函数或交换函数体。

        这是否是可取的和良好的做法,我对此表示怀疑。类被密封是有原因的。由于 .net 框架对字符串做出的一些假设,在某些情况下执行我提到的操作可能会导致未定义的行为。字符串类内部崩溃的可能性很大。

        【讨论】:

          【解决方案7】:

          10 年后,您可以(在某种程度上)使用包装类和隐式转换来做到这一点。
          但仅仅因为你可以并不意味着你应该。

          这是一些代码:

              // implements all interfaces that string does through the field content
              public sealed class StringWrapper : IEnumerable<char>, ICloneable, IComparable, IComparable<string>, IConvertible, IEquatable<string>
              {
                  private readonly string content;
          
                  private StringWrapper(string content)
                  {
                      this.content = content;
                  }
          
                  // implicit conversions
                  public static implicit operator string(StringWrapper d) => d.content;
                  public static implicit operator StringWrapper(string b) => new StringWrapper(b);
          
                  public static bool operator <(StringWrapper lhs, StringWrapper rhs)
                  {
                      return lhs.content.CompareTo(rhs.content) < 0;
                  }
          
                  public static bool operator >(StringWrapper lhs, StringWrapper rhs)
                  {
                      return lhs.content.CompareTo(rhs.content) > 0;
                  }
          
                  // string supports it, why shouldnt we?
                  public static StringWrapper operator +(StringWrapper lhs, StringWrapper rhs)
                  {
                      var sb = new StringBuilder();
                      sb.Append(lhs.content);
                      sb.Append(rhs.content);
                      return sb.ToString();
                  }
          
                  // at request of @Alexey Khoroshikh
                  public static StringWrapper operator *(StringWrapper lhs, int rhs)
                  {
                      var sb = new StringBuilder();
                      for (int i = 0; i < rhs; i++)
                      {
                          sb.Append(lhs.content);
                      }
                      return sb.ToString();
                  }
          
                  // other nice thing to have
                  public static string[] operator /(StringWrapper lhs, char rhs)
                  {
                      return lhs.content.Split(rhs);
                  }
          
                  public override bool Equals(object obj)
                  {
                      return (obj is StringWrapper wrapper && content == wrapper.content)
                          || (obj is string str && content == str);
                  }
          
                  #region auto-generated code through visual studio
          
                  public override int GetHashCode()
                  {
                      return -1896430574 + EqualityComparer<string>.Default.GetHashCode(content);
                  }
          
                  public override string ToString()
                  {
                      return this.content;
                  }
          
                  public object Clone()
                  {
                      return content.Clone();
                  }
          
                  public int CompareTo(string other)
                  {
                      return content.CompareTo(other);
                  }
          
                  public bool Equals(string other)
                  {
                      return content.Equals(other);
                  }
          
                  public IEnumerator<char> GetEnumerator()
                  {
                      return ((IEnumerable<char>)content).GetEnumerator();
                  }
          
                  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
                  {
                      return ((System.Collections.IEnumerable)content).GetEnumerator();
                  }
          
                  public int CompareTo(object obj)
                  {
                      return content.CompareTo(obj);
                  }
          
                  public TypeCode GetTypeCode()
                  {
                      return content.GetTypeCode();
                  }
          
                  public bool ToBoolean(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToBoolean(provider);
                  }
          
                  public byte ToByte(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToByte(provider);
                  }
          
                  public char ToChar(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToChar(provider);
                  }
          
                  public DateTime ToDateTime(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToDateTime(provider);
                  }
          
                  public decimal ToDecimal(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToDecimal(provider);
                  }
          
                  public double ToDouble(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToDouble(provider);
                  }
          
                  public short ToInt16(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToInt16(provider);
                  }
          
                  public int ToInt32(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToInt32(provider);
                  }
          
                  public long ToInt64(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToInt64(provider);
                  }
          
                  public sbyte ToSByte(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToSByte(provider);
                  }
          
                  public float ToSingle(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToSingle(provider);
                  }
          
                  public string ToString(IFormatProvider provider)
                  {
                      return content.ToString(provider);
                  }
          
                  public object ToType(Type conversionType, IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToType(conversionType, provider);
                  }
          
                  public ushort ToUInt16(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToUInt16(provider);
                  }
          
                  public uint ToUInt32(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToUInt32(provider);
                  }
          
                  public ulong ToUInt64(IFormatProvider provider)
                  {
                      return ((IConvertible)content).ToUInt64(provider);
                  }
          
                  #endregion auto-generated code through visual studio
              }
          
          

          【讨论】:

          • 我不敢相信你在十年前就发布了这个问题的答案!干得好,我的朋友。
          猜你喜欢
          • 1970-01-01
          • 2015-11-12
          • 2017-11-04
          • 2012-09-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多