C# String Operator Overloading
Asked Answered
B

7

9

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

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

}

Bueschel answered 6/4, 2010 at 19:20 Comment(3)
FYI: Partial classes need to reside in the same assembly (project).Suicide
Those operator are already defined for strings so it make no sense to redefine them. It could break a lot of code if it was possible.Scarlett
Those operators are not defined. At least for c#. They exist in VB. Maybe that's why OP needs them. That's the reason I came here for.Heard
D
18

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
Daegal answered 6/4, 2010 at 19:23 Comment(3)
Even if he could inherit from it, only instances that were of his inherited type and referenced as the inherited type would be able to use the alternate operators. Because operators are not polymorphic (they're overloaded, not overridden) even referencing the subtype as a string would eliminate the alternate operator functionality.Simitar
@Adam Robinson: exactly. Bottom line: @ScottSEA simply cannot do what he wants to do.Daegal
I know your post isn't about extension methods, but it might be useful to provide a link. To someone new to C#, it looks like it's being called wrong. msdn.microsoft.com/en-ca/library/vstudio/bb383977.aspxIshmaelite
B
18

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.

Bander answered 6/4, 2010 at 20:7 Comment(3)
Thanks for that answer. As a keenly adhered design decision , it would explain why custom string operators weren't possible ten years ago and still aren't to this day.Latreshia
What I would be looking for is rather to represent String.Concat(Enumerable.Repeat("Hello", 4)) as "Hello" * 4 Does anybody know if that's possible in any way?Alfreda
@AlexeyKhoroshikh: Nope. There is no type upon which you could define that operator.Bander
S
10

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.

Simitar answered 6/4, 2010 at 19:23 Comment(0)
P
5

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.

Preussen answered 6/4, 2010 at 19:23 Comment(0)
O
3
  • 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.

Organzine answered 6/4, 2010 at 19:24 Comment(0)
E
1

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.

Eggplant answered 6/4, 2010 at 19:25 Comment(0)
N
-1

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
    }

Newsman answered 8/9, 2020 at 17:32 Comment(1)
I cannot believe you posted this answer to a question ten years old! Well done, my friend.Bueschel

© 2022 - 2024 — McMap. All rights reserved.