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
精彩评论