Shirik
Shirik

Reputation: 3701

C# Generic Operators

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?

Upvotes: 29

Views: 31364

Answers (6)

Marc Gravell
Marc Gravell

Reputation: 1062770

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

Upvotes: 8

Rakoo
Rakoo

Reputation: 546

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

Upvotes: 10

tomatoRadar
tomatoRadar

Reputation: 401

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.

Upvotes: 1

Ark-kun
Ark-kun

Reputation: 6787

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) { ... }

Upvotes: 3

R. Martinho Fernandes
R. Martinho Fernandes

Reputation: 234444

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

Upvotes: 23

Matt Mitchell
Matt Mitchell

Reputation: 41823

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.

Upvotes: 2

Related Questions