C
C#2y ago
Alexicon

Using the new INumber interface to determine if an object is a number without having the generic arg

With .net7 we now have the INumber<TSelf> interface which is pretty cool, and I thought I found a place where I might be able to use it for the first time. However, I am running into some trouble. I am wondering if there is any way to know if some ‘object’ is a number and return the ‘One’ abstract static property from the INumber<TSelf> interface it would implement. The issue is I do not have a generic argument to plug in for TSelf, and I cannot add one in this case since the method I am working on is an override of a method outside of my control and it does not have a T generic argument. Here is a cut-down example as a x-unit test of what I am doing and what I have tried.
[Theory]
[InlineData(1)]
[InlineData(2.2)]
[InlineData("not a number")]
public object? MyMethod(object something)
{
Type numberType = typeof(INumber<>);
Type somethingType = something.GetType();

//this fails if something is not a number since TSelf must implement INumber<TSelf> :(
Type genericNumberType = numberType.MakeGenericType(somethingType);

bool isNumber = something
.GetType()
.IsAssignableTo(genericNumberType);

if (isNumber)
{
//all of these and more result in nothing
//i have tried many diffrent permutations of BindingFlags for each of these
var nothing = genericNumberType.GetProperties(BindingFlags.Static);
var alsoNothing = genericNumberType.GetMembers(BindingFlags.Static);
var againNothing = something.GetType().GetMembers(BindingFlags.Static);

//the crux of what i want:
//INumber<int>.One = 1
//INumber<double>.One = 1.0
object? result = somethingType
.GetProperty("One")?
.GetValue(something);

return result;
}

return null;
}
[Theory]
[InlineData(1)]
[InlineData(2.2)]
[InlineData("not a number")]
public object? MyMethod(object something)
{
Type numberType = typeof(INumber<>);
Type somethingType = something.GetType();

//this fails if something is not a number since TSelf must implement INumber<TSelf> :(
Type genericNumberType = numberType.MakeGenericType(somethingType);

bool isNumber = something
.GetType()
.IsAssignableTo(genericNumberType);

if (isNumber)
{
//all of these and more result in nothing
//i have tried many diffrent permutations of BindingFlags for each of these
var nothing = genericNumberType.GetProperties(BindingFlags.Static);
var alsoNothing = genericNumberType.GetMembers(BindingFlags.Static);
var againNothing = something.GetType().GetMembers(BindingFlags.Static);

//the crux of what i want:
//INumber<int>.One = 1
//INumber<double>.One = 1.0
object? result = somethingType
.GetProperty("One")?
.GetValue(something);

return result;
}

return null;
}
Unfortunately, with INumber<TSelf> being so new there is not a lot of information about it online so before I give up, I figured I would see if anyone here knows how to achieve this.
6 Replies
Alexicon
AlexiconOP2y ago
According to 'https://stackoverflow.com/questions/73588532/access-abstract-interface-member-c11' these abstract members might only be accessible via their generic types. For example ‘T.One‘ rather than say ‘int.One’ or ‘INumber<int>.One’. This would tell me this is probably not possible, but I figured I would still see if anyone had anything.
Stack Overflow
Access Abstract Interface Member C#11
I'm trying to get the new AdditiveIdentity property from all of the inbuilt INumber<T> classes. So I'm starting with int (which should return 0). However, I'm finding it difficult to actually...
Thinker
Thinker2y ago
You can't do this. You need the generic type (or reflection).
Alexicon
AlexiconOP2y ago
right, I guess what I was trying to figure out was how to do it with reflection, which I was unable to make work.
vdvman1
vdvman12y ago
Maybe try having a generic method that you call with reflection which does the actual work
Alexicon
AlexiconOP2y ago
Excellent suggestion @vdvman1, Doing the following does in fact work for getting the ‘One’ abstract interface property from any of these number types. Very clever. Now I just need to figure out how to know if some Type is implementing the INumber interface before calling this.
[Theory]
[InlineData(1, (int)1)]
[InlineData(2.2, (double)1)]
[InlineData(0x7fffffffffffffffL, (long)1)]
public void GetOne(object input, object expected)
{
Type inputType = value.GetType();

var genericMethodInfo = StaticGetOneMethodInfo.MakeGenericMethod(inputType);

object? actual = genericMethodInfo.Invoke(null, null);

Assert.Equal(expected, actual); //all passing
}
private static MethodInfo? _staticGetOneMethodInfo;
private static MethodInfo StaticGetOneMethodInfo => _staticGetOneMethodInfo ??= (typeof(MyUnitTest).GetMethod(nameof(StaticGetOne), BindingFlags.Static | BindingFlags.NonPublic) ?? throw new Exception("The method was not found"));
private static T StaticGetOne<T>() where T : INumber<T> => T.One;
[Theory]
[InlineData(1, (int)1)]
[InlineData(2.2, (double)1)]
[InlineData(0x7fffffffffffffffL, (long)1)]
public void GetOne(object input, object expected)
{
Type inputType = value.GetType();

var genericMethodInfo = StaticGetOneMethodInfo.MakeGenericMethod(inputType);

object? actual = genericMethodInfo.Invoke(null, null);

Assert.Equal(expected, actual); //all passing
}
private static MethodInfo? _staticGetOneMethodInfo;
private static MethodInfo StaticGetOneMethodInfo => _staticGetOneMethodInfo ??= (typeof(MyUnitTest).GetMethod(nameof(StaticGetOne), BindingFlags.Static | BindingFlags.NonPublic) ?? throw new Exception("The method was not found"));
private static T StaticGetOne<T>() where T : INumber<T> => T.One;
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server