开发者

C# String Operator Overloading

G'Day Mates -

What is the right way (excluding the argument of whether it is advisable) to overload the string operators <, >, <= and >= ?

I've tried it five ways to Sunday and I get various errors - my best shot was declaring a partial class and overloading from there, but it won't work for some reason.

namespace System
{
   public partial class String
   {
       public static Boolean operator <(String a, String b)
       {
           return a.CompareTo(b) < 0;
 开发者_StackOverflow中文版      }

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

}


String is a sealed class. You cannot inherit from it, and without the original source for String, you cannot compile a partial class of it. Even if you got your hands on the source (it's possible via Reflector or via Visual Studio symbol download) you'd still have problems, since it's a first class citizen in the runtime.

Do you really need < and > as operators on string? If so... why not just use 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


There is no way to replace any built-in behaviour of the compiler with your own. You cannot override existing built-in operators for comparisons, conversions, arithmetic, and so on. That's by design; it's so that someone can read your code and know that int x = M(); int y = x + 2; does integer arithmetic, as opposed to, say, formatting your hard disk.

Can you explain why you want to do this? Perhaps there is a better way to do what you want.


The simple answer is that you can't; there's no way to modify the operators for another class. Partial classes are only allowed for classes that are both declared partial in all files and defined in the same assembly.


Do you mean the System.String class? That's impossible in C#. You can't add extension operators to an existing class. It's a much requested feature though.


  • You can't have a partial class for string, because the string class itself is not partial and hence won't work with your partial class.

  • String is sealed, so you can inherit from it and then overload the operator.

In short, alas, you can't do what you with to do.

Not know what you're trying to do exactly, I can't suggest a good alternative. But, take a look at extension methods, which are often good for situations. Leaving aside whether you should :), you could add to the string class a method called IsGreaterThan and return true or false however you wish. This is good because you can give the extension method a name that makes it's meaning clear, keeps the existing operators in tact (which you have no choice about anyway), and allows for quick/simple code.


You cannot directly overload the >= and <= operator, but you can achieve the same result by overload the > and the == separately.

Your code seems correct to me, except the fact that you miss the overloading for ==.

Seems I was wrong, however, you can always fall back to reflection. I think if you do some digging and hacking, you can make it work with reflection to extend the class since reflection allows to add functions or swap function bodies at runtime.

Whether it is advisable and good practice, I doubt it. There is a reason why the class is sealed. Doing the thing I mentioned may result in undefined behavior in some circumstances due to some assumptions the .net framework makes on strings. Chances are big that the string class will collapse internally.


After 10 years you can do it (to some degree) using a wrapper class and implicit conversions.
But just because you can doesn't mean you should.

Here it is some code:

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜