如何通过reflection代码(c#)设置可空类型?
我需要使用reflection设置类的属性。
我有一个Dictionary
带有属性名和字符串值。
在reflection循环中,我需要在为每个属性设置值时将字符串值转换为适当的属性类型。 其中一些属性类型是可空类型。
- 如果属性是可以为空的类型,我怎么能从PropertyInfo中知道?
- 如何使用reflection设置可空类型?
编辑:在这个博客的评论中定义的第一个方法似乎也可以做到这一点: http : //weblogs.asp.net/pjohnson/archive/2006/02/07/437631.aspx
-
一种方法是:
type.GetGenericTypeDefinition() == typeof(Nullable<>)
-
只需设置为任何其他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); }