I try to implement type testing which supports null.
I have two generic functions:
public static int FromValueType<T>(T? o) where T : struct { /* do stuff */ return 1; }
public static int FromClassType<T>(T o) where T : class { /* do stuff */ return 2; }
I understand that neither of the two can support null
because the compiler can not infer T
from null
. For example, this call won't work:
var x = FromClassType(null);
This is why I would like to write a wrapper which implements type testing to extend the functionality for null
.
What i have tried so far:
public static int From(object o)
{
return o switch
{
null => 0,
/**
* TODO: Compiler complains:
* The type arguments for method 'int MyModule.FromValueType<T>(T?)'
* cannot be inferred from the usage. Try specifying the type arguments
* explicitly.
*/
ValueType oValueType => FromValueType(oValueType),
_ => FromClassType(o)
};
}
But this does not work either.
If ValueType
is a base class for all value types, why can the compiler not infer T
?
CodePudding user response:
System.ValueType
is a reference type. Therefore the compile can not infer a matching version of:
public static int FromValueType<T>(T? o) where T : struct { /* do stuff */ return 1; }
As far as I know, there exists no common base type for all value types. This means we have to use reflection to build and call FromValueType
at runtime:
public static int From(object o)
{
return o switch
{
null => 0,
ValueType oValueType => (int) typeof(ClassWithFromValueTypeMethod)
.GetMethod(nameof(FromValueType))
?.MakeGenericMethod(oValueType.GetType())
.Invoke(null, new []{o}),
_ => FromClassType(o)
};
}
Using reflection in this situation might not be optimal but its the only solution I can see that works without refactoring FromValueType
and FromClassType
.
If we can can refactor FromValueType
and FromClassType
. It is probably best to simply move the logic from the two inside the From
wrapper:
public static int From<T>(T o)
{
if (o == null) return 0;
return typeof(T).IsValueType
? 1 // FromValueType
: 2; // FromClassType
}
Note that depending on the complexity of FromValueType
and FromClassType
this might be a less ideal solution in terms of testability and maintainabilty. Also it does not support calling it with null
. To support that, we have to do some runtime type checking again:
public static int From(object o)
{
if (o == null) return 0;
return o.GetType().IsValueType
? 1 // FromValueType
: 2; // FromClassType
}
Thank you @JonSkeet for your helpful comment.