如何通过反射代码(c#)设置可空类型?
我需要使用反射设置类的属性 我有一个如何通过反射代码(c#)设置可空类型?,c#,reflection,C#,Reflection,我需要使用反射设置类的属性 我有一个字典,里面有属性名和字符串值 在反射循环中,我需要将字符串值转换为适当的属性类型,同时为每个属性设置值。其中一些属性类型是可为空的类型 如何从PropertyInfo知道属性是否为可空类型 如何使用反射设置可为null的类型 编辑: 本博客评论中定义的第一种方法似乎也起到了同样的作用: 检查可空类型很容易,int?实际上是System.Nullable。 因此,您只需检查该类型是否是System.Nullable的泛型实例。 设置不应该有区别,nullable
字典
,里面有属性名和字符串值
在反射循环中,我需要将字符串值转换为适当的属性类型,同时为每个属性设置值。其中一些属性类型是可为空的类型
检查可空类型很容易,int?实际上是
System.Nullable
。
因此,您只需检查该类型是否是System.Nullable
的泛型实例。
设置不应该有区别,nullableProperty.SetValue(实例,null)
或nullableProperty.SetValue(实例,3)
type.GetGenericTypeDefinition() == typeof(Nullable<>)
为什么需要知道它是否可以为null?您是指“引用类型”还是“可为空的” 无论哪种方式,对于字符串值,最简单的选项都是通过
类型转换器
,这在PropertyDescriptor
上更容易(更准确)获得:
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj);
// then per property...
PropertyDescriptor prop = props[propName];
prop.SetValue(obj, prop.Converter.ConvertFromInvariantString(value));
即使按属性(而不是按类型)设置,也应使用正确的转换器。最后,如果你做了很多这方面的工作,这允许通过加速,而不改变代码(除了为类型启用它,只执行一次)。我创建了一个小示例。如果您对此代码有任何疑问,请添加注释 编辑:根据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);
}
}
}
}
具体而言,要将整数转换为枚举并分配给可为空的枚举属性,请执行以下操作:
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);
}
最初,在中提到了最佳解决方案。然而,当您需要实现动态解决方案时,您不知道一个类上可以声明多少个可为null的字段,最好的办法是检查是否可以将可为null的类型分配给 属性,通过反射检查该属性
protected T initializeMe<T>(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");
}
...
}
...
}
受保护的T初始化项(T实体,值)
{
类型eType=entity.GetType();
foreach(eType.GetProperties()中的PropertyInfo pi)
{
//获取并命名DataRow中列的名称
Type valueType=pi.GetType();
if(值!=System.DBNull.value)
{
pi.SetValue(实体、值、空);
}
else if(valueType.IsGenericType&&typeof(Nullable).IsAssignableFrom(valueType))//检查是否可以将Nullable分配给proptety
{
pi.SetValue(实体,null,null);
}
其他的
{
System.Diagnostics.Trace.WriteLine(“此处某物”);
}
...
}
...
}
以下是“可为空”对象类型的最安全解决方案
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);
}
我使用了下面的解决方案,避免使用类型转换器来控制代码 我编写了一个支持操作的助手类
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
public static class ObjectExtensions
{
/// <summary>
/// Enable using reflection for setting property value
/// on every object giving property name and value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="target"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool SetProperty<T>(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;
}
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
运用系统反思;
使用系统文本;
公共静态类对象扩展
{
///
///启用使用反射设置属性值
///在每个对象上指定属性名称和值。
///
///
///
///
///
///
公共静态bool SetProperty(此T目标、字符串propertyName、对象值)
{
PropertyInfo pi=target.GetType().GetProperty(propertyName);
if(pi==null)
{
Assert(false);
返回false;
}
尝试
{
//将要设置的值转换为正确的类型
value=ConvertValue(pi.PropertyType,value);
//使用正确的类型设置值
pi.SetValue(目标、值、空);
}
捕获(例外情况除外)
{
Assert(false);
返回false;
}
返回true;
}
私有静态对象值(类型propertyType,对象值)
{
//检查需要处理的每种类型
//通过这种方式,您可以在赋值之前控制转换操作
if(propertyType==typeof(int)||
propertyType==typeof(int?)
{
int值;
if(int.TryParse(value.ToString(),out intValue))
value=intValue;
}
else if(propertyType==typeof(字节)||
propertyType==typeof(字节?)
{
字节字节值;
if(byte.TryParse(value.ToString(),out byteValue))
值=字节值;
}
else if(propertyType==typeof(string))
{
value=value.ToString();
}
其他的
{
//扩展您自己的句柄类型
Assert(false);
}
返回值;
}
}
注意:设置可空值(例如int?)时,该值几乎必须是整数或可转换类型。不能在字节?上设置int。因此,需要正确转换。请参阅ConvertValue()代码,该代码检查类型(int)和相应的可空类型(int?)
这是用于设置具有所需数据结构(字典)的值的代码
public class Entity
{
public string Name { get; set; }
public byte? Value { get; set; }
}
static void SetNullableWithReflection()
{
// Build array as requested
Dictionary<string, string> props = new Dictionary<string, string>();
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);
}
公共类实体
{
公共字符串名称{get;set;}
公共字节?值{get;set;}
}
静态void SetNullableWithReflection()
{
//按要求生成阵列
字典道具=新字典();
添加(“姓名”、“名字”);
道具。增加(“价值”、“1”);
//实体
实体=新实体();
//为每个属性指定一个值
foreach(道具中的var项目)
entity.SetProperty(item.Key,item.Value);
//检查结果
public class Entity
{
public string Name { get; set; }
public byte? Value { get; set; }
}
static void SetNullableWithReflection()
{
// Build array as requested
Dictionary<string, string> props = new Dictionary<string, string>();
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);
}