5

Possible Duplicate:
C# generic constraint for only integers

As you can see in the following code, I need to compute the sum of two generic numbers.

public class NumberContainer<T>
{
    public T ValueA { get; private set; }
    public T ValueB { get; private set; }
    public T Total { get { return ValueA + ValueB; } }
}

However, it isn't possible to do a direct addition of the two T values, which results in the compiler error below :

Operator '+' cannot be applied to operands of type 'T' and 'T'

Given that I don't intend to use T for anything else than value-types that represent numbers (short, ushort, int, uint, etc), how could I perform the sum? (efficiency is a factor to be considered)

Community
  • 1
  • 1
  • Or possibly: [Is there a C# generic constraint for “real number” types?](http://stackoverflow.com/questions/1348594/is-there-a-c-sharp-generic-constraint-for-real-number-types/1348625#1348625) – M.Babcock Feb 18 '12 at 04:05
  • 1
    The question is closed as a duplicate of questions seeking to put a constraint on `T`, which is *not* what this question is trying to do. I think that it should be reopened. – Sergey Kalinichenko Feb 19 '12 at 03:50

4 Answers4

11

You can do it with "little magic" from LINQ:

private static readonly Func<T, T, T> adder;
static NumberContainer() {
    var p1 = Expression.Parameter(typeof (T));
    var p2 = Expression.Parameter(typeof (T));
    adder = (Func<T, T, T>)Expression
        .Lambda(Expression.Add(p1, p2), p1, p2)
        .Compile();
} 
public T Total { get { return adder(ValueA, ValueB); } }

The only drawback is that this code will compile even if NumberContainer is instantiated with a type T that does not support addition; of course it will throw an exception at run-time. An added benefit is that this should work with user-defined + operators.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
6

In addition to using LINQ, if you're on .NET 4 this is possible using dynamic:

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

In your code sample this turns into:

public class NumberContainer<T> where T: struct
{
    public T ValueA { get; private set; }
    public T ValueB { get; private set; }
    public T Total { get { return ((dynamic)ValueA) + ((dynamic)ValueB); } }
}

This approach doesn't ensure safety though. If T is some random struct that doesn't support + you'll end up with an exception (I think).

M.Babcock
  • 18,753
  • 6
  • 54
  • 84
  • Scary use of dynamic, especially considering operators are not virtual... I guess it'll "just work" by it's magic box. (I would have preferred structural types *as well* :-/) –  Feb 18 '12 at 04:55
  • @pst - Agreed, but it does happen to work. I understand the point though. – M.Babcock Feb 18 '12 at 04:59
0

You can specify a constraint for T that it must be a struct, and perhaps that it implements IConvertable, and then use Convert.

public class NumberContainer<T>
        where T : struct, IConvertible
    {
        public T ValueA { get; private set; }
        public T ValueB { get; private set; }
        public T Total {
            get
            {
                // do type checking here, then:

                return (T)Convert.ChangeType(
                    Convert.ToDouble((object)ValueA) +
                    Convert.ToDouble((object)ValueB), typeof(T));
            } 
        }
    }

However, there's no way with generics to guarantee that T is an integer or floating type at compile time. You can check it at runtime, though, and throw an exception.

moribvndvs
  • 42,191
  • 11
  • 135
  • 149
  • Did it off the top of my head, fixed and compiles. If you'd like to further elaborate on why it won't work, that'd be a lot more helpful. – moribvndvs Feb 18 '12 at 04:19
  • DateTime is a struct and a IConvertible but cannot be converted into double so will just throw an exception anytime you will call it with DateTime parameters. – brainfood May 09 '16 at 10:45
0

You could treat this just as an abstract base class and derive specific classes that specified the T value and provided the implementation for Total

public abstract class NumberContainer<T>
    where T: struct
{
    public T ValueA { get; private set; }
    public T ValueB { get; private set; }
    public abstract T Total();
}

public class IntContainer : NumberContainer<int>
{
    public override int Total()
    {
        return ValueA + ValueB;
    }
}
Michael Christensen
  • 1,768
  • 1
  • 13
  • 11