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

Time:11-21

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.

  • Related