Detecting a Nullable Type via reflection
Asked Answered
B

9

42

Surprisingly the following code fails the Assert:

int? wtf = 0;
Assert.IsType<Nullable<int>>(wtf);

So just out curiosity, how can you determine if a given instance is a Nullable<> object or not?

Bhatt answered 17/5, 2011 at 5:58 Comment(2)
possible duplicate of How to check if an object is nullable?Eccentricity
If you're also looking for NRT (C# 8) reflection, have a look at Namotion.Reflection, github.com/RicoSuter/Namotion.ReflectionAnnabal
E
80

Well firstly, Nullable<T> is a struct, so there isn't an object as such. You can't call GetType(), as that will box the value (at which point you either get null and thus an exception, or a boxed non-nullable value and therefore not the type you want).

(Boxing is what's messing up your assertion here - I would assume that IsType accepts object.)

You can use type inference though to get the type of the variable as a type parameter:

public bool IsNullable<T>(T value)
{
    return Nullable.GetUnderlyingType(typeof(T)) != null;
}

That's not a huge amount of use when you know the exact type at compile-time as in your example, but it's useful for generics. (There are alternative ways of implementing it, of course.)

What's your real life situation? I assume it's not an assertion like this, given that you know the answer to this one at compile time.

Enthalpy answered 17/5, 2011 at 6:3 Comment(5)
Very tricky! That appears to do it. Now I just have to remember why I wanted to know that...Bhatt
Also I never realized that structs don't have a GetType() but I should have figured that out since I understand that they get boxed for such things.Bhatt
@justin.m.chase: Structs inherit GetType from object - but because the call is non-virtual, they can't override it, which means the value is always boxed... which then screws things up when the original value is nullable.Enthalpy
To see if it is any nullable type you can do this: (type.IsGenericType && type.GetGenericTypeDefinition().Equals( typeof(Nullable<>) ));Gatefold
@Andre: Yes, but I find using Nullable.GetUnderlyingType simpler :)Enthalpy
S
11

I like the @jon-skeet answer but it only works if you know the type you are testing against. In our world we are using reflection to open up objects and test values against regex expressions.

simplifying the extension to work for any type worked better for us.

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

generics are life's blood but sometimes... :)

Seasickness answered 11/11, 2016 at 15:25 Comment(1)
This would cause a boxing operation when you tried to get the Type of a struct though right? Compared to Jon's.Bhatt
H
5
int? i = 0;
var type = TypedReference.GetTargetType(__makeref(i));
var isNullable = type.IsGenericType &&
    type.GetGenericTypeDefinition() == typeof(Nullable<>);
Harridan answered 14/2, 2014 at 13:11 Comment(1)
This is brilliant, it gives the declared type (not underlying type as in object i = 0; i.GetType() == typeof(int);). But if it is to get declared type why not make us of type inference simply? Like, public Type GetDeclaredType<T>(this T t) { return typeof(T); }. Much more readable.Eccentricity
O
3

Only this way worked in my case using .net core 7

MyClass mclass = new MyClass();    

PropertyInfo[] properties = mclass.GetType().GetProperties();

foreach (PropertyInfo propertyInfo in properties)
{
   bool nullable = propertyInfo.GetMethod is null ? false : new NullabilityInfoContext().Create(propertyInfo.GetMethod.ReturnParameter).ReadState == NullabilityState.Nullable;
   if (nullable)
      {
        //some script to do
      }
}
Outpost answered 7/1, 2023 at 21:20 Comment(3)
I think you need to expand this to show how you're getting the propertyInfo.Bhatt
@Bhatt Thanks for your comment, Answer is updated now after expanding the code to clearify how did i get the propertyInfo var.Outpost
This is useful for PropertyInfo cases, which can mask the nullability behind the Get method implementation.Gallop
I
0

What namespace is Assert in?

The following returns true as you would expect:

int? wtf = 0;
if (typeof(Nullable<int>).IsInstanceOfType(wtf))
{
    // Do things
}

Although its worth noting that typeof(Nullable<int>).IsInstanceOfType(42) also returns true - this is because this method accepts an object and so gets boxed as a Nullable<int>.

Ironware answered 17/5, 2011 at 6:5 Comment(8)
Assert in my case was in Xunit but I was just trying to say in general that that statement should succeed.Bhatt
Also, I don't think the above works... well technically it returns true but I believe it's a false positive because this also returns true: typeof(Nullable<int>).IsInstanceOfType(0)Bhatt
@Kragen: Try it with int wtf = 0; and int? wtf = null; for some surprising results. Boxing makes it impossible for such a method signature to really do what you want. EDIT: After your edit, there's still a problem - 42 does not get boxed as a Nullable<int>. Rather, 0 gets boxed as an int boxed type. There is no way of constructing a boxed value of a nullable type.Enthalpy
@Jon How come typeof(Nullable<int>) return true for an int boxed type in that case?Ironware
@Kragen: It looks like Type.IsInstanceOfType is basically treating nullable types and non-nullable types as equivalent. Try typeof(int).IsInstanceOfType(wtf) with your initial code, for example. I can't immediately find this documented, which is rather worrying...Enthalpy
@Jon I've figured it out by looking at the implementation using Reflection - its because typeof(int?).IsAssignableFrom() is true. (Seems like a bit of an odd choice, especially after having read the MSDN documentation)Ironware
@Kragen: 0 is int? same thing puzzled me too, first time I saw it.Spheroidicity
@Rick Looking at the documentation 0 is int? makes sense (you can cast an int to an int?), but 0 isn't actually an instance of int?.Ironware
R
0

Here is what I came up with, as everything else seemed to fail - at least on Portable Class Library / DotNet Core with >= C# 6

Basically you extend generic Type Object and Nullable<T> and use the fact that the static extension method that matches the underlying type is going to be invoked and takes precedence over the generic T extension-method.

public static partial class ObjectExtension
{
    public static bool IsNullable<T>(this T self)
    {
        return false;
    }
}

and one for Nullable<T>

public static partial class NullableExtension
{
    public static bool IsNullable<T>(this Nullable<T> self) where T : struct
    {
        return true;
    }
}

Using Reflection and type.IsGeneric and type.GetGenericParameters() did not work on my current set of .NET Runtimes.

Ranjiv answered 25/8, 2016 at 5:12 Comment(0)
S
0

It works fo me, hope for you too.

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}
Sayer answered 22/1, 2021 at 11:21 Comment(0)
C
0

Based on Vladimir answer:

public static class GenericExtensions
{
   public static bool IsNullable<T>(this T item) =>
     TypedReference.GetTargetType(__makeref(item)).FullName.Contains("System.Nullable");
}

Usage:

int? nullableInt = 42;
bool nIntIsNullable = nullableInt.IsNullable();

Duration: <2ms on average machine.

Remarks:

Casaleggio answered 7/1, 2022 at 11:10 Comment(0)
A
0

This works for me to check wether a type is Nullable or not..

type.Assembly.FullName.StartsWith("System") && type.Name.Contains("Nullable");
Apples answered 23/8, 2022 at 18:10 Comment(2)
Gross but checks out.Bhatt
@Bhatt it is not that great but to check whether a prop is UserDefined we may need to check type.Assembly.FullName.StartsWith("System") && prop.Type.IsClass. So it ain't that badApples

© 2022 - 2024 — McMap. All rights reserved.