开发者

Determine if reflected property can be assigned null

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 th开发者_运维技巧e value, so using exception handling wrapped around SetValue is not an option.


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.


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

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

Type would be your PropertyInfo.PropertyType


PropertyInfo propertyInfo = ...
bool canAssignNull = 
    !propertyInfo.PropertyType.IsValueType || 
    propertyInfo.PropertyType.IsGenericType &&
        propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)


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.


Suppose we have a class:

public class MyClass
{
    // Should tell me I cannot assign a null
    public int Age { get; set; }
    public DateTime BirthDate { get; set; }
    public object Family { get; set; }
    // Should tell me I can assign a null
    public DateTime? DateOfDeath { get; set; }
}

In fact we could assign null to Family property, but as we have not write

public object? Family { get; set; }

we consider it mandatory and want to avoid set it null. Nevertheless if we try offered decision we get True answer:

foreach (PropertyInfo pi in typeof(MyClass).GetProperties())
{
    bool canBeNull = !pi.PropertyType.IsValueType 
          || (Nullable.GetUnderlyingType(pi.PropertyType) != null);
    Console.WriteLine($"{pi.Name}, {canBeNull}");
}

gives us:

Age, False
BirthDate, False
Family, True
DateOfDeath, True

We could use:

bool canBeNull = !pi.PropertyType.IsValueType 
  && pi.GetCustomAttributes().Any(a => a.GetType().Name.Contains("NullableAttribute"))
            || (Nullable.GetUnderlyingType(pi.PropertyType) != null);

Now try:

foreach (PropertyInfo pi in typeof(MyClass).GetProperties())
{
    bool canBeNull = !pi.PropertyType.IsValueType 
         && pi.GetCustomAttributes().Any(a => a.GetType().Name.Contains("NullableAttribute"))
            || (Nullable.GetUnderlyingType(pi.PropertyType) != null);
        
    Console.WriteLine($"{pi.Name}, {canBeNull}");
}

and get:

Age, False
BirthDate, False
Family, False
DateOfDeath, True
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜