如何通过reflection代码(c#)设置可空类型?

我需要使用reflection设置类的属性。

我有一个Dictionary带有属性名和字符串值。

在reflection循环中,我需要在为每个属性设置值时将字符串值转换为适当的属性类型。 其中一些属性类型是可空类型。

  1. 如果属性是可以为空的类型,我怎么能从PropertyInfo中知道?
  2. 如何使用reflection设置可空类型?

编辑:在这个博客的评论中定义的第一个方法似乎也可以做到这一点: http : //weblogs.asp.net/pjohnson/archive/2006/02/07/437631.aspx

  1. 一种方法是:

     type.GetGenericTypeDefinition() == typeof(Nullable<>) 
  2. 只需设置为任何其他reflection代码:

     propertyInfo.SetValue(yourObject, yourValue); 

为什么你需要知道它是否可以为空? 你的意思是“引用类型”,还是“ Nullable ”?

无论哪种方式,使用字符串值,最简单的选项是通过TypeConverter ,这在PropertyDescriptor上更容易(并且更准确)可用:

 PropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj); // then per property... PropertyDescriptor prop = props[propName]; prop.SetValue(obj, prop.Converter.ConvertFromInvariantString(value)); 

这应该使用正确的转换器,即使设置per-property(而不是per-type)。 最后,如果您正在做很多这样的事情,这允许通过HyperDescriptor加速,而无需更改代码(除了为类型启用它,仅执行一次)。

具体来说,将整数转换为枚举并分配给可以为空的枚举属性:

 int value = 4; if(propertyInfo.PropertyType.IsGenericType && Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null && Nullable.GetUnderlyingType(propertyInfo.PropertyType).IsEnum) { var enumType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); var enumValue = Enum.ToObject(enumType, value); propertyInfo.SetValue(item, enumValue, null); //-- suggest by valamas //propertyInfo.SetValue(item, (value == null ? null : enumValue), null); } 

我创造了小样本。 如果您对此代码有任何疑问,请添加评论。

编辑 :根据Marc Gravell的精彩评论更新样本

 class Program { public int? NullableProperty { get; set; } static void Main(string[] args) { var value = "123"; var program = new Program(); var property = typeof(Program).GetProperty("NullableProperty"); var propertyDescriptors = TypeDescriptor.GetProperties(typeof(Program)); var propertyDescriptor = propertyDescriptors.Find("NullableProperty", false); var underlyingType = Nullable.GetUnderlyingType(propertyDescriptor.PropertyType); if (underlyingType != null) { var converter = propertyDescriptor.Converter; if (converter != null && converter.CanConvertFrom(typeof(string))) { var convertedValue = converter.ConvertFrom(value); property.SetValue(program, convertedValue, null); Console.WriteLine(program.NullableProperty); } } } } 

最初,在MSDN论坛上提到了最佳解决方案。 但是,当您需要实现动态解决方案时,您不确切地知道可以在类上声明多少可空字段,最好的办法是检查Nullable <>类型是否可以分配给属性,您可以通过该属性进行检查反射

 protected T initializeMe(T entity, Value value) { Type eType = entity.GetType(); foreach (PropertyInfo pi in eType.GetProperties()) { //get and nsame of the column in DataRow Type valueType = pi.GetType(); if (value != System.DBNull.Value ) { pi.SetValue(entity, value, null); } else if (valueType.IsGenericType && typeof(Nullable<>).IsAssignableFrom(valueType)) //checking if nullable can be assigned to proptety { pi.SetValue(entity, null, null); } else { System.Diagnostics.Trace.WriteLine("something here"); } ... } ... } 

我使用了以下解决方案,避免使用类型转换器来更好地控制代码。

我写了一个辅助类来支持操作

 using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Text; public static class ObjectExtensions { ///  /// Enable using reflection for setting property value /// on every object giving property name and value. ///  ///  ///  ///  ///  ///  public static bool SetProperty(this T target, string propertyName, object value) { PropertyInfo pi = target.GetType().GetProperty(propertyName); if (pi == null) { Debug.Assert(false); return false; } try { // Convert the value to set to the properly type value = ConvertValue(pi.PropertyType, value); // Set the value with the correct type pi.SetValue(target, value, null); } catch (Exception ex) { Debug.Assert(false); return false; } return true; } private static object ConvertValue(Type propertyType, object value) { // Check each type You need to handle // In this way You have control on conversion operation, before assigning value if (propertyType == typeof(int) || propertyType == typeof(int?)) { int intValue; if (int.TryParse(value.ToString(), out intValue)) value = intValue; } else if (propertyType == typeof(byte) || propertyType == typeof(byte?)) { byte byteValue; if (byte.TryParse(value.ToString(), out byteValue)) value = byteValue; } else if (propertyType == typeof(string)) { value = value.ToString(); } else { // Extend Your own handled types Debug.Assert(false); } return value; } } 

注意:当你设置一个可以为空的值(例如int?)时,该值几乎必须是整数或可转换类型。你不能在一个字节上设置int?所以,你需要正确转换 。参见ConvertValue()代码,检查type(int)和相应的可空类型(int?))

这是使用所需数据结构Dictionary设置值的代码。

  public class Entity { public string Name { get; set; } public byte? Value { get; set; } } static void SetNullableWithReflection() { // Build array as requested Dictionary props = new Dictionary(); props.Add("Name", "First name"); props.Add("Value", "1"); // The entity Entity entity = new Entity(); // For each property to assign with a value foreach (var item in props) entity.SetProperty(item.Key, item.Value); // Check result Debug.Assert(entity.Name == "First name"); Debug.Assert(entity.Value == 1); } 

检查Nullable类型很容易,int? 实际上是System.Nullable 。 因此,您只需检查类型是否为System.Nullable的通用实例。 设置应该没有区别, nullableProperty.SetValue(instance, null)nullableProperty.SetValue(instance, 3)

这是“可空”对象类型最安全的解决方案

 if (reader[rName] != DBNull.Value) { PropertyInfo pi = (PropertyInfo)d[rName.ToLower()]; if (pi.PropertyType.FullName.ToLower().Contains("nullable")) pi.SetValue(item, reader[rName]); else pi.SetValue(item, Convert.ChangeType(reader[rName], Type.GetType(pi.PropertyType.FullName)), null); }