47

I wish to automagically discover some information on a provided class to do something akin to form entry. Specifically I am using reflection to return a PropertyInfo value for each property. I can read or write values to each property from my "form", but if the property is defined as "int", I would not be able to, and my program should not even try, to write a null value.

How can I use reflection to determine if a given property can be assigned a null value, without writing a switch statement to check for every possible type? In particular I want to detect the difference between boxed types like "int" vs. "int?", since in the second case I do want to be able to write a null value. The IsValueType and IsByRef don't seem to see a difference.

public class MyClass
{
    // Should tell me I cannot assign a null
    public int Age {get; set;} 
    public DateTime BirthDate {get; set;}
    public MyStateEnum State {get; set;}
    public MyCCStruct CreditCard {get; set;}

    // Should tell me I can assign a null
    public DateTime? DateOfDeath {get; set;}
    public MyFamilyClass Famly {get; set;}
}

Note that I need to determine this information long before I actually attempt to write the value, so using exception handling wrapped around SetValue is not an option.

David
  • 24,700
  • 8
  • 63
  • 83

4 Answers4

109

You need to handle null references and Nullable<T>, so (in turn):

bool canBeNull = !type.IsValueType || (Nullable.GetUnderlyingType(type) != null);

Note that IsByRef is something different, that allows you to choose between int and ref int / out int.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Something I didn't think about but I did test your answer against is arrays, such as int[] (which can be assigned null). Your answer worked correctly for this case as well. – David Nov 20 '09 at 12:52
  • 2
    Arrays are reference-types, so they work the same as classes. – Marc Gravell Nov 20 '09 at 12:54
  • I think that it should be noted that in the OP, there was examples using an int? and DateTime?, so the first check would return true (!type.IsValueType). I do like this way better than the one given in MSDN, but curious why you added what seems to be a redundant check. Maybe, because you want to deal with all cases beyond what was asked for? – pqsk Jul 31 '13 at 21:55
  • 2
    @pqsk because the questions is "...can be assigned null"; need to consider reference types – Marc Gravell Aug 01 '13 at 07:33
  • @MarcGravell this is just a coincidence, wasn't stalking you. I am unable to access the `IsValueType` but I do find it in the debugger. Do I have to use a cast? – Neville Nazerane Dec 07 '17 at 02:04
  • just found it. for .net core and .net standard this property is here `type.GetTypeInfo().IsValueType` – Neville Nazerane Dec 07 '17 at 02:06
10

From http://msdn.microsoft.com/en-us/library/ms366789.aspx

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))

Type would be your PropertyInfo.PropertyType

Jonas Lincoln
  • 9,567
  • 9
  • 35
  • 49
5
PropertyInfo propertyInfo = ...
bool canAssignNull = 
    !propertyInfo.PropertyType.IsValueType || 
    propertyInfo.PropertyType.IsGenericType &&
        propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)
user215303
  • 157
  • 1
  • 2
  • Using PropertyInfo has more overhead than using the type directly. But may be helpful if you need the extra features for other things down the road. – TamusJRoyce Sep 27 '17 at 14:49
1

Marc and Jonas both have parts to determine if a generic type can be assigned null.

// A silly example. default(T) will return null if it is nullable. So no reason to check here. Except for the sake of having an example.
public U AssignValueOrDefault<U>(object item)
{
    if (item == null)
    {
        Type type = typeof(U); // Type from Generic Parameter

        // Basic Types like int, bool, struct, ... can't be null
        //   Except int?, bool?, Nullable<int>, ...
        bool notNullable = type.IsValueType ||
                           (type.IsGenericType && type.GetGenericTypeDefinition() != typeof(Nullable<>)));

        if (notNullable)
            return default(T);
    }

    return (U)item;
}

Note: Most of the time you can check if the variable is null. Then use default(T). It will return null by default of the object is a class.

TamusJRoyce
  • 817
  • 1
  • 12
  • 25