6

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)

2

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

Comments

7

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

2 Comments

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 :-/)
@pst - Agreed, but it does happen to work. I understand the point though.
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.

2 Comments

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

2 Comments

If an int could be converted to T then this wouldn't be a problem. Your code doesn't compile.
silly me. Updated answer to provide an alternate solution since there are now a few solutions using dynamics and link