C# Generic Operators
Asked Answered
I

6

29

I am trying to implement a generic operator like so:

class Foo
{
   public static T operator +<T>(T a, T b) 
   {
       // Do something with a and b that makes sense for operator + here
   }
}

Really what I'm trying to do is gracefully handle inheritance. With a standard operator + in Foo, where T is instead "Foo", if anyone is derived from Foo (say Bar inherits Foo), then a Bar + Bar operation will still return a Foo. I was hoping to solve this with a generic operator +, but I just get a syntax error for the above (at the <) making me believe that such code is not legal.

Is there a way to make a generic operator?

Issuant answered 6/5, 2011 at 0:23 Comment(1)
A workable (albeit less programmer-friendly) workaround could be a named method that takes a generic parameter: public static T Add<T>(T a, T b) { //Implementation goes here }. This could be used like Foo x = new Bar(); Foo y = new MyClass(); Foo sum = Foo.Add(x, y);.Dailey
T
23

No, you can't declare generic operators in C#.

Operators and inheritance don't really mix well.

If you want Foo + Foo to return a Foo and Bar + Bar to return a Bar, you will need to define one operator on each class. But, since operators are static, you won't get the benefits of polymorphism because which operator to call will be decided at compile-time:

Foo x = new Bar();
Foo y = new Bar();
var z = x + y; // calls Foo.operator+;
Torquay answered 6/5, 2011 at 0:26 Comment(5)
Unfortunate, but simple enough. Thanks.Issuant
Shirik, you should accept Martinho's answer, if it was the answer to your question.Doublet
I should, but since I can't accept an answer for the first 15 minute, I won't yet.Issuant
I'm not so sure I buy your "consider another problematic scenario" though. Honestly, operators are not special in any way. They're just methods with an unusual name. Your question could equally be changed to the case where I have 3 methods, which take "Foo, Foo", "Bar, Bar", or "Qux, Qux" as parameters. Which one would it take? Obviously the "Foo, Foo" one, because it's the only one that satisfies the types. But I don't need to buy into why they chose to make this restriction. I just accept that it is a restriction and move on.Issuant
@Shirik: yeah, you're right about that. That example was more of a possible bad design than of a problem with operators. Removed it.Torquay
V
10

https://jonskeet.uk/csharp/miscutil/usage/genericoperators.html

static T Add<T>(T a, T b) {
    //TODO: re-use delegate!
    // declare the parameters
    ParameterExpression paramA = Expression.Parameter(typeof(T), "a"),
        paramB = Expression.Parameter(typeof(T), "b");
    // add the parameters together
    BinaryExpression body = Expression.Add(paramA, paramB);
    // compile it
    Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
    // call it
    return add(a,b);       
}
Verbalism answered 25/3, 2014 at 12:28 Comment(2)
Talk about unreadable. So much abstraction, and very little intent (exposed). Isn't maintainability just as important as re-usability?Axiomatic
@barrypicker, strongly agreed, i do think maintainability comes first. code is code, for human beings, it must be readable firstRanique
B
7

This is now possible in C# 11 via static abstract interface methods; for example:

public interface IMyInterface<T> where T : IMyInterface<T>
{
    static abstract T operator +(T left, T right);
}

which you can then use via where T : IMyInterface<T> as:

class Bar
{
    static T Add<T>(T x, T y, T z) where T : IMyInterface<T>
    {
        return x + y + z;
    }
}

The problem, though, is that every T you want would need to implement IMyInterface<T>, which isn't possible for pre-defined types (like int, float, etc) which you don't control. The good news is that .NET 7 does this for you, for all the types you might think of, so in reality you don't need to define your own API; instead, you might use the system-defined interface INumber<T>:

    static T Add<T>(T x, T y, T z) where T : INumber<T>
    {
        return x + y + z;
    }
Birecree answered 14/12, 2022 at 9:41 Comment(0)
N
3

You can just define operator in a generic class Foo.

You can also create real generic operators, but C# compiler won't use them.

[System.Runtime.CompilerServices.SpecialName]
public static T op_Addition<T>(T a, T b) { ... }
Nathalie answered 10/10, 2012 at 10:2 Comment(2)
C# compiler will not use nongeneric tooCarlsen
@Carlsen What did you mean?Nathalie
T
2

You cannot declare generic operators in C# - I am not sure on the reasoning but assume it's a usefulness vs effort thing for the implementation team (I believe there might be a post on here with Jon Skeet discussing it, or perhaps on his blog when he discussed things he'd like to see in C#).

Indeed, you cannot even use operators with generics in C#.

This is because generics must be applicable for all possible types that could be provided. This is why you must scope the generic type to classes when you want to use == as below:

void IsEqual<T>(T x, T y) where T : class
{
    return x == y;
}

Unfortunately you cannot do:

void Add<T>(T x, T y)  where T : operator +
{
    return x + y;
}

You might also be interested in this short summary article I came across.

Tuxedo answered 6/5, 2011 at 0:32 Comment(1)
You can however, wrap the latter's return x + y; bit in an abstract method, forcing the child to implement it. This is still not ideal, since it would retain the parent's typing, and not the child class's typing.Zimmermann
M
1

Was searching for the same thing and google brought me here.... I wasn't too happy about the accepted answer and was looking for a workaround.

I managed to implement this using generics. Here is the Foo and Bar class:

    class Foo
    {
        private int value;

        public Foo(int x)
        {
            value = x;
        }
        public virtual int getVal()
        {
            return value;
        }
    }

    class Bar : Foo
    {
        private int derivedValue;

        public Bar(int x):base(x) 
        {
            derivedValue = x;
        }
        public override int getVal()
        {
            return derivedValue;
        }
    }

Then a generic class containing the operators but restricted to type of Foo and derived from Foo:

    class GenericOp<T> where T : Foo
    {
        private T value;

        public GenericOp(T x)
        {
            value = x;
        }
        public static Foo operator +(GenericOp<T> a, GenericOp<T> b) 
        {
            return new Foo(a.value.getVal() + b.value.getVal());
        }
    }

Some usage code showing you always get back a Foo as well as preventing you from mixing the types:

Foo f1 = new Foo(1);
Foo f2 = new Foo(2);
Bar b1 = new Bar(10);
Bar b2 = new Bar(20);

GenericOp<Foo> left = new GenericOp<Foo>(f1);
GenericOp<Foo> right = new GenericOp<Foo>(f2);
Foo res = left + right;

GenericOp<Bar> left1 = new GenericOp<Bar>(b1);
GenericOp<Bar> right1 = new GenericOp<Bar>(b2);
Foo res1 = left1 + right1;

GenericOp<Foo> left2 = new GenericOp<Foo>(f1);
GenericOp<Bar> right2 = new GenericOp<Bar>(b1);
//Foo res2 = left2 + right2; //this fails and rightfully so.
Maribelmaribelle answered 22/3, 2015 at 6:10 Comment(2)
Isn't this basically just making a wrapper around a specific type? The original post is describing a full generic solution, not a solution restricted to a narrow type. It would be a lot less code, more maintain able, and be cheaper to have Foo use a constructor overload and do the math on construction. With this approach, you avoid the creation of additional objects per result and avoid a whole class.Bashuk
@AWinkle: well not really....The original post is looking for operations on a type and classes derived from that type. And as R. Martinho Fernandes has mentioned: "since operators are static, you won't get the benefits of polymorphism because which operator to call will be decided at compile-time. " Whereas here you always get back the type you want at runtime.Maribelmaribelle

© 2022 - 2024 — McMap. All rights reserved.