How can you implement a function as follows?
static object GetZero(Type t)
{
// If t implements INumberBase<TSomething>, then return INumberBase<TSomething>.Zero
// Otherwise, throw some arbitrary Exception
}
My attempt so far was:
static object GetZero(Type t)
{
return t
.GetInterfaces()
.Single(x => x.IsGenericType && !x.IsGenericTypeDefinition && x.GetGenericTypeDefinition() == typeof(INumberBase<>))
.GetProperty("Zero", BindingFlags.Public | BindingFlags.Static)
.GetValue(null);
}
However, under dotnet7, this fails with a fairly nasty exception. For example, GetZero(typeof(int)) throws
System.BadImageFormatException : Bad IL format. at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.PropertyInfo.GetValue(Object obj)
Why is this implementation throwing? Is there another way to implement this via reflection?
Edit: I can work around this by adding a static method as follows
static T Zero<T>() where T : INumberBase<T>
{
return T.Zero;
}
Then having my reflection code invoke this instead of the interface property. This works, but seems .. ugly? So I'm still curious why the original code above throws.
Static abstract interface members can be invoked only from concrete type, i.e. something like var i = INumberBase<int>.Zero;
(which your reflection code basically attempts to do) is invalid and will not compile.
Personally I prefer the generic indirection approach (the Zero<T>
one, you can combine it with the reflection if needed) as far more robust, but pure reflection approach can be something like the following:
static object GetZero(Type t) => t
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
.Where(info => info.Name.EndsWith(".Zero") || info.Name = "Zero")
.Single()
.GetValue(null);
Though it relies on the implementation details and is quite brittle (you can improve it by checking if type is INumberBase<>
and searching first for member with name like INumberBase< TYPE_NAME >.Zero
and then for name = Zero
, but this quickly becomes more ugly than the generic approach).
See also this answer.