Home > Back-end >  How to have a generic variable to INumber in .NET 7?
How to have a generic variable to INumber in .NET 7?

Time:11-19

We can use the new INumber<TSelf> interface in .NET 7 to reference any numeric type, like the following:

using System.Numerics;

INumber<int> myNumber = 72;
INumber<float> mySecondNumber = 93.63f;

However, because of the type constraint in INumber, we can't have a generic reference that can hold any numeric type. This following code is invalid:

using System.Numerics;

INumber myNumber = 72;
myNumber = 93.63f;

How can I have an array of any numeric objects and call a method that is expecting a INumber<TSelf> object.

using System.Numerics;

object[] numbers = new object[] { 1, 2.5, 5, 0x1001, 72 };

for (int i = 0; i < numbers.Length - 1; i  )
{
    Console.WriteLine("{0} plus {1} equals {2}", numbers[i], numbers[i   1], AddNumbers(numbers[i], numbers[i   1]));
}

static T AddNumbers<T>(T left, T right) where T : INumber<T> => left   right;

CodePudding user response:

How can I have an array of any numeric objects and call a method that is expecting a INumber object.

The thing is that array must have all elements of the same type. Simply because array is just a memory block and i-th element is a place in memory located at address arrayStart i*(elementSize). It just won’t work if sizes are different.

Therefore for value types it is not possible(they might have different sizes), but it is possible to have array of objects, then every element can have any type(will be boxed in case of value type).

So, you would need to create array of objects, where you can put float, int, whatever.

Also I don’t think it makes much sense to have common interface for all numbers, because if you you want to add float to long, how should you do that - cast to float or long? It is just much clearer to convert numbers to the most convenient type for the task.

CodePudding user response:

It's not possible. The INumber<TSelf> type is declared like that:

public interface INumber<TSelf> : 
    IComparable, 
    IComparable<TSelf>, 
    IEquatable<TSelf>, 
    IParsable<TSelf>, 
    ISpanParsable<TSelf>,
    System.Numerics.IAdditionOperators<TSelf,TSelf,TSelf>,
    System.Numerics.IAdditiveIdentity<TSelf,TSelf>,
    System.Numerics.IComparisonOperators<TSelf,TSelf,bool>,
    System.Numerics.IDecrementOperators<TSelf>,
    System.Numerics.IDivisionOperators<TSelf,TSelf,TSelf>,
    System.Numerics.IEqualityOperators<TSelf,TSelf,bool>,
    System.Numerics.IIncrementOperators<TSelf>,
    System.Numerics.IModulusOperators<TSelf,TSelf,TSelf>, 
    System.Numerics.IMultiplicativeIdentity<TSelf,TSelf>, 
    System.Numerics.IMultiplyOperators<TSelf,TSelf,TSelf>, 
    System.Numerics.INumberBase<TSelf>,        
    System.Numerics.ISubtractionOperators<TSelf,TSelf,TSelf>, 
    System.Numerics.IUnaryNegationOperators<TSelf,TSelf>, 
    System.Numerics.IUnaryPlusOperators<TSelf,TSelf> 
    where TSelf : INumber<TSelf>

As you can see, all the interfaces use the TSelf type. So the INumber interface does not have a contract that supports operations between different types but only operations within the same type.

Since you have a list of mixed types, the compiler has no chance to check whether the actual types of operands at runtime are a supported combination.

CodePudding user response:

tl;dr: you can't.


You noticed that there's no non-generic type INumber which INumber<TSelf> implements, because this would cause havoc.

You know something bad is happening in your code when you have to declare

var numbers = new object[] { 1, 2.5, 5, 0x1001, 72 };

to hold your INumber<T> values.

You also couldn't declare, e.g. something like

var numbers = new INumber<>[] { 1, 2.5, 5, 0x1001, 72 };

because you'd encounter CS7003: "Unexpected use of an unbound generic name"


Suppose you have a type, UserDefinedNumber : INumber<UserDefinedNumber>, and that there exists a non-generic interface type INumber.

For INumber to be useful it'd have to have things like operators - but these would also have to be non-generic - so a non-generic version of the IAdditionOperators<TSelf,TOther,TResult> interface which defines the operator - it'd have to take INumber, INumber as its arguments.

Now, suppose you had

INumber a = 1d;
INumber b = new UserDefinedNumber(...);
var c = a   b;

Now - what would you expect a b to do?

Since the left- and right-hand side of the operator are both typed as INumber, the compiler would use the implementation of the operator on a (which is a double) but since that's a built-in type there's no way that it'd have any logic to handle adding a double to a UserDefinedNumber.

  • Related