如何对支持null的引用类型和值类型进行类型测试?

How can I implement type testing for reference types and value types supporting null?

我尝试实现支持 null 的类型测试。

我有两个通用函数:

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; }

我理解这两个都不支持null,因为编译器无法从null推断出T。例如,此调用将不起作用:

var x = FromClassType(null);

这就是为什么我想编写一个包装器来实现类型测试以扩展 null 的功能。

到目前为止我尝试了什么:

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)
   };
}

但这也不行。

如果 ValueType 是所有值类型的基础 class,为什么编译器不能推断 T

System.ValueType 是引用类型。因此编译无法推断出匹配的版本:

public static int FromValueType<T>(T? o) where T : struct { /* do stuff */ return 1; }

据我所知,所有值类型都不存在共同的基类型。这意味着我们必须在运行时使用反射来构建和调用 FromValueType:

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) 
    };  
}

在这种情况下使用反射可能不是最佳选择,但这是我能看到的唯一无需重构即可工作的解决方案 FromValueTypeFromClassType.

如果我们可以重构 FromValueTypeFromClassType。最好简单地从 From 包装器中的两个逻辑移动:

public static int From<T>(T o)
{   
    if (o == null) return 0;
    return typeof(T).IsValueType
        ? 1  // FromValueType
        : 2; // FromClassType
}

请注意,根据 FromValueTypeFromClassType 的复杂性,就可测试性和可维护性而言,这可能不是一个理想的解决方案。它也不支持用 null 调用它。为了支持这一点,我们必须再次进行一些运行时类型检查:

public static int From(object o)
{   
    if (o == null) return 0;
    return o.GetType().IsValueType
        ? 1  // FromValueType
        : 2; // FromClassType
}

感谢@JonSkeet 的有用评论。