Solution for Overloaded Operator Constraint in .Net Generics

Solution for overloaded operator constraint in .NET generics

There is no immediate answer; operators are static, and cannot be expressed in constraints - and the existing primatives don't implement any specific interface (contrast to IComparable[<T>] which can be used to emulate greater-than / less-than).

However; if you just want it to work, then in .NET 3.5 there are some options...

I have put together a library here that allows efficient and simple access to operators with generics - such as:

T result = Operator.Add(first, second); // implicit <T>; here

It can be downloaded as part of MiscUtil

Additionally, in C# 4.0, this becomes possible via dynamic:

static T Add<T>(T x, T y) {
dynamic dx = x, dy = y;
return dx + dy;
}

I also had (at one point) a .NET 2.0 version, but that is less tested. The other option is to create an interface such as

interface ICalc<T>
{
T Add(T,T)()
T Subtract(T,T)()
}

etc, but then you need to pass an ICalc<T>; through all the methods, which gets messy.

operator overloading with generics

The problem is the compiler cannot know if + operator can be applied to T. Unfortunately, there is no way to constraint T to be a numeric type in C#.

However, you might be able to workaround this using the dynamic runtime:

public class Complex<T> where T : struct
{
public T _a, _b;
public Complex(T i, T j)
{
_a = i;
_b = j;
}
public static Complex<T> operator +(Complex<T> i, Complex<T> j)
{
return new Complex<T>(Sum(i._a, j._a), Sum(i._b, j._b));
}

private static T Sum(T a, T b)
{
return (dynamic)a + (dynamic)b;
}
}

Is there a generic constraint I could use for the + operator?

There are no such devices in C#. A few options are available, though:

  • in C# 4.0 and .NET 4.0 (or above), use dynamic, which supports + but offers no compile time checking
  • in .NET 3.5 (or above), MiscUtil offers an Operator class which makes operators available as methods - again, without any compile-time checking

So either:

return (dynamic)left.Evaluate(context) + (dynamic)right.Evaluate(context);

or

return Operator.Add(left.Evaluate(context), right.Evaluate(context));

How to use Generics to implement this?

Unrestricted generics like your Calc<T> must be able to compile with any type applied, not all types support the + operand so your code does not compile. This is regardless of what specific types you create your calling code with.

You can restrict the type of T and thus gain access to more methods by doing Calc<T> where T:object or Calc<T> where T: IComparable which would allow you to:

public T CompareTo(T a, T b)
{
return (a.CompareTo(b));
}

Since all T must now implement IComparable. Unfortunately Int32 does not implement any interface which defines the + operator or any addition method. So there is no way to implement that statement you are trying.

Define a generic that implements the + operator

This is a pretty commonly requested new feature for C#: the ability to specify more generic parameter constraints than the ones we already have. Operators are among the most frequently asked. However, C# does not currently support this.

Possible workarounds:

  • Pass a delegate to any method that needs to do addition. This is the most type-safe option, but of course it’s annoying if you need to call such a method often. For example:

    public class Generic<T> {
    public void DoSomething(T anItem, T anotherItem, Func<T, T, T> add) {
    // instead of
    Blah(anItem + anotherItem);
    // have to write:
    Blah(add(anItem, anotherItem));
    }
    }

    Generic<int> genInt = ...;
    // and then instead of ...
    genInt.DoSomething(1, 2);
    // have to write:
    genInt.DoSomething(1, 2, (a, b) => a + b);
  • Declare your own interface IAddable. Then you can use it as a generic type parameter constraint, but obviously you can’t use int as the parameter then. You would have to use a struct of your own that contains just an int and which implements IAddable:

    public interface IAddable<T> {
    T Add(T other);
    }
     
    public struct Integer : IAddable<Integer> {
    public int Value;
    public Integer(int value) { Value = value; }
    public Integer Add(Integer other) { return new Integer(Value + other.Value); }
    }

    // then instead of
    Generic<int> blah = ...;
    // have to write:
    Generic<Integer> blah = ...;
  • dynamic. Another possible workaround is to use dynamic, but this is rather hacky and completely unsafe: it will let you pass in any type and call any method or operator, and only crash at runtime, not at compile-time.

Arithmetic operator overloading for a generic class in C#

I think the best you'd be able to do is use IConvertible as a constraint and do something like:

 public static operator T +(T x, T y)
where T: IConvertible
{
var type = typeof(T);
if (type == typeof(String) ||
type == typeof(DateTime)) throw new ArgumentException(String.Format("The type {0} is not supported", type.FullName), "T");

try { return (T)(Object)(x.ToDouble(NumberFormatInfo.CurrentInfo) + y.ToDouble(NumberFormatInfo.CurrentInfo)); }
catch(Exception ex) { throw new ApplicationException("The operation failed.", ex); }
}

That won't stop someone from passing in a String or DateTime though, so you might want to do some manual checking - but IConvertible should get you close enough, and allow you to do the operation.

Is it possible in C# to overload a generic cast operator in the following way?

Conversion operators can't be generic. From the spec section 10.10, here's the format of a conversion-operator-declarator:

conversion-operator-declarator:
implicit operator type ( type identifier )
explicit operator type ( type identifier )

Compare this with, say, a method-header:

method-header:
attributesopt method-modifiersopt partialopt return-type
member-name type-parameter-listopt ( formal-parameter-listopt )
type-parameter-constraints-clausesopt

(Sorry about the formatting - not sure how to do it better.)

Note that the operator format doesn't include a type parameter list or type parameter constraints.



Related Topics



Leave a reply



Submit