Given a generic class definition like
public class ConstrainedNumber<T> :
IEquatable<ConstrainedNumber<T>>,
IEquatable<T>,
IComparable<ConstrainedNumber<T>>,
IComparable<T>,
IComparable where T:struct, IComparable, IComparable<T>, IEquatable<T>
How can I define arithmetic operators for it?
The following does not compile, because the '+' operator cannot be applied to types 'T' and 'T':
public static T operator +( ConstrainedNumber<T> x, ConstrainedNumber<T> y)
{
return x._value + y._value;
}
The generic type 'T' is constrained with the 'where' keyword as you can see, but I need a constraint for number types that have arithmetic operators (IArithmetic?).
'T' will be a primitive number type such as int, float, etc. Is there a 'where' constraint for such types?
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.
Unfortunately there is no way to constrain a generic parameter to be an integral type (Edit: I guess "arithmetical type" might be a better word as this does not pertain to just integers).
It would be nice to be able to do something like this:
where T : integral // or "arithmetical" depending on how pedantic you are
or
where T : IArithmetic
I would suggest that you read Generic Operators by our very own Marc Gravell and Jon Skeet. It explains why this is such a difficult problem and what can be done to work around it.
.NET 2.0 introduced generics into the .NET world, which opened the door for many elegant solutions to existing problems. Generic constraints can be used to restrict the type-arguments to known interfaces etc, to ensure access to functionality - or for simple equality/inequality tests the Comparer.Default and EqualityComparer.Default singletons implement IComparer and IEqualityComparer respectively (allowing us to sort elements for instance, without having to know anything about the "T" in question).
With all this, though, there is still a big gap when it comes to operators. Because operators are declared as static methods, there is no IMath or similar equivalent interface that all the numeric types implement; and indeed, the flexibility of operators would make this very hard to do in a meaningful way. Worse: many of the operators on primitive types don't even exist as operators; instead there are direct IL methods. [emphasis mine] To make the situation even more complex, Nullable<> demands the concept of "lifted operators", where the inner "T" describes the operators applicable to the nullable type - but this is implemented as a language feature, and is not provided by the runtime (making reflection even more fun).
In C# 4.0 you can use dynamic to get round this limitation. I had a look at your code, and managed to produce a working (albeit cutdown) version:
public class ConstrainedNumber<T> where T : struct, IComparable, IComparable<T>, IEquatable<T>
{
private T _value;
public ConstrainedNumber(T value)
{
_value = value;
}
public static T operator +(ConstrainedNumber<T> x, ConstrainedNumber<T> y)
{
return (dynamic)x._value + y._value;
}
}
And a little test program to go with it:
class Program
{
static void Main(string[] args)
{
ConstrainedNumber<int> one = new ConstrainedNumber<int>(10);
ConstrainedNumber<int> two = new ConstrainedNumber<int>(5);
var three = one + two;
Debug.Assert(three == 15);
Console.ReadLine();
}
}
Enjoy!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With