Operator Overloading with C# Extension Methods
Asked Answered
D

7

193

I'm attempting to use extension methods to add an operater overload to the C# StringBuilder class. Specifically, given StringBuilder sb, I'd like sb += "text" to become equivalent to sb.Append("text").

Here's the syntax for creating an extension method for StringBuilder:

public static class sbExtensions
{
    public static StringBuilder blah(this StringBuilder sb)
    {
        return sb;
    }
} 

It successfully adds the blah extension method to the StringBuilder.

Unfortunately, operator overloading does not seem to work:

public static class sbExtensions
{
    public static StringBuilder operator +(this StringBuilder sb, string s)
    {
        return sb.Append(s);
    }
} 

Among other issues, the keyword this is not allowed in this context.

Are adding operator overloads via extension methods possible? If so, what's the proper way to go about it?

Demivolt answered 5/10, 2008 at 20:59 Comment(1)
Although this at first seems like a cool idea, consider var otherSb = sb + "hi";Gaut
B
165

This is not currently possible, because extension methods must be in static classes, and static classes can't have operator overloads. But the feature is being discussed for some future release of C#. Mads talked a bit more about implementing it in this video from 2017.

On why it isn't currently implemented, Mads Torgersen, C# Language PM says:

...for the Orcas release we decided to take the cautious approach and add only regular extension methods, as opposed to extention properties, events, operators, static methods, etc etc. Regular extension methods were what we needed for LINQ, and they had a syntactically minimal design that could not be easily mimicked for some of the other member kinds.

We are becoming increasingly aware that other kinds of extension members could be useful, and so we will return to this issue after Orcas. No guarantees, though!

Further below in the same article:

I am sorry to report that we will not be doing this in the next release. We did take extension members very seriously in our plans, and spent a lot of effort trying to get them right, but in the end we couldn't get it smooth enough, and decided to give way to other interesting features.

This is still on our radar for future releases. What will help is if we get a good amount of compelling scenarios that can help drive the right design.

Butterball answered 5/10, 2008 at 21:4 Comment(10)
This page has since been taken down; this issue is still not resolved.Landslide
Too bad. I just wanted to add an operator to multiply a TimeSpan by a scalar value... :(Polyphonic
I was hoping to implement this same concept to cast a String to a PowerShell ScriptBlock.Avenue
That means I can't overload % to be xor betwen booleans? :( dead at true.Xor(false) thenHarriott
@Harriott ^ is the xor operator in C#Butterball
@JacobKrall Hmmm I tought it was only bitwise and not a logical operator too. Thanks!Harriott
Is this still unders consideration? It seems that it's still not possible to overload operators using extension methods.Kellerman
@Kellerman yes, still under consideration, but not for c# 8.0. It didn't make the cutJelene
Probably not in C# 9 either ... learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9Debonair
Any updates for 22-23?Technician
W
63

If you control the places where you want to use this "extension operator" (which you normally do with extension methods anyway), you can do something like this:

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

} 

The StringBuilderWrapper class declares an implicit conversion operator from a StringBuilder and declares the desired + operator. This way, a StringBuilder can be passed to ReceiveImportantMessage, which will be silently converted to a StringBuilderWrapper, where the + operator can be used.

To make this fact more transparent to callers, you can declare ReceiveImportantMessage as taking a StringBuilder and just use code like this:

  private static void ReceiveImportantMessage(StringBuilder sb) {
    StringBuilderWrapper sbw = sb;
    sbw += "Hello World!";
  }

Or, to use it inline where you're already using a StringBuilder, you can simply do this:

 StringBuilder sb = new StringBuilder();
 StringBuilderWrapper sbw = sb;
 sbw += "Hello World!";
 Console.WriteLine(sb.ToString());

I created a post about using a similar approach to make IComparable more understandable.

Wenonawenonah answered 3/2, 2010 at 19:8 Comment(11)
I think you meant that StringBuilderWrapper should inherit StringBuilder ?Glynda
@Leon: I really meant to compose it, not inherit from it. Anyway, I couldn't inherit from it since it's sealed.Tetra
fair enough, but you're passing a StringBuilder to ReceiveImportantMessage, which expects a StringBuilderWrapperGlynda
@Leon: That's the heart of this technique. I can do that because there's an implicit conversion operator declared in StringBuilderWrapper that makes it possible.Tetra
This is required to create a new type, and cannot be used with literals like " " .Haugh
@pylover: You're right, this requires creating a new type, that will wrap the StringBuilder type and provide an implicit conversion operator from it. After that, it can be used with string literals, as demonstrated in the example: sb += "Hello World!";Tetra
@Jordão: Yes, But i wish to overload the *operator for string literal, and use it in T4 templates like PushIndent(" " * 4) just like the python.Haugh
May I suggest then, to add an extension method to String: PushIndent(" ".X(4)) (could also be called Times). Or maybe using this constructor: PushIndent(new String(' ', 4)).Tetra
@Jordão: Great answer ;)Laboured
Great answer, ty!!Winther
Suggest also overloading "*" to perform AppendLine().Motorboat
E
8

It appears this isn't currently possible - there's an open feedback issue requesting this very feature on Microsoft Connect:

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

suggesting it might appear in a future release but isn't implemented for the current version.

Eyeleteer answered 5/10, 2008 at 21:5 Comment(3)
What exactly do you mean, "isn't currently possible?" It must be possible in the CLR because F# supports extension everything.Spoonbill
I think he means that it is not possible in C#, not the CLR. The whole extension methods thing is a C# compiler trick anyway.Innis
Link is dead now.Isidor
V
2

Though it's not possible to do the operators, you could always just create Add (or Concat), Subtract, and Compare methods....

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


}
Varices answered 17/5, 2012 at 21:18 Comment(0)
S
2

Hah! I was looking up "extension operator overloading" with exactly the same desire, for sb += (thing).

After reading the answers here (and seeing that the answer is "no"), for my particular needs, I went with an extension method which combines sb.AppendLine and sb.AppendFormat, and looks tidier than either.

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

And so,

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

Not a general answer, but may be of interest to future seekers looking at this kind of thing.

Schaal answered 28/6, 2014 at 18:14 Comment(1)
I think your extension method would read better if you named it AppendLine instead of Line.Dian
H
0

Its possible to rigg it with a wrapper and extensions but impossible to do it properly. You end with garbage which totally defeats the purpose. I have a post up somewhere on here that does it, but its worthless.

Btw All numeric conversions create garbage in string builder that needs to be fixed. I had to write a wrapper for that which does work and i use it. That is worth perusing.

Halimeda answered 27/1, 2017 at 23:51 Comment(0)
S
0

The previous answers still seem to be correct, this is not implemented in C#. You can, however, get very close with a little more code.

Option 1, wrapper class around StringBuilder

Something like this:

public static class ExtStringBuilder
{
    public class WStringBuilder
    {
        private StringBuilder _sb { get; }
        public WStringBuilder(StringBuilder sb) => _sb = sb;
        public static implicit operator StringBuilder(WStringBuilder sbw) => sbw._sb;
        public static implicit operator WStringBuilder(StringBuilder sb) => new WStringBuilder(sb);
        public static WStringBuilder operator +(WStringBuilder sbw, string s) => sbw._sb.Append(s);
    }
    public static WStringBuilder wrap(this StringBuilder sb) => sb;
}

usage like this:

StringBuilder sb = new StringBuilder();
// requires a method call which is inconvenient
sb = sb.wrap() + "string1" + "string2";

Or:

WStringBuilder sbw = new StringBuilder();
sbw = sbw + "string1" + "string2";
// you can pass sbw as a StringBuilder parameter
methodAcceptsStringBuilder(sbw);
// but you cannot call methods on it
// this fails: sbw.Append("string3");

You can make a wrapper for string instead of StringBuilder in the exact same fashion, which might be a better fit, but in this case, you'll be wrapping possibly many string objects instead of 1 StringBuilder object.


As another answer points out, this strategy seems to defeat a lot of the purpose of having an operator since depending on the use case, the way you use it changes. So it feels a little awkward.

In this case, I agree, this is far too much overhead just to replace a method like StringBuilder's Append method. However, if you are putting something more complicated behind the operator (something requiring a for loop, for instance), you may find this is a convenient solution for your project.

Sclerophyll answered 26/12, 2022 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.