【问题标题】:how to set nullable type via reflection code ( c#)?如何通过反射代码(c#)设置可空类型?
【发布时间】:2010-10-01 14:01:10
【问题描述】:

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

我有一个Dictionary<string,string>,其中包含属性名称和字符串值。

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

  1. 我如何从 PropertyInfo 知道该属性是否为可空类型?
  2. 如何使用反射设置可为空的类型?

编辑: 此博客上的 cmets 中定义的第一个方法似乎也可以解决问题: http://weblogs.asp.net/pjohnson/archive/2006/02/07/437631.aspx

【问题讨论】:

  • 我怀疑你的意思是“cast” - 例如,你不能将字符串“cast”为整数。请参阅我的答案重新转换。
  • 你是对的 - 我的意思是转换 - 修正了文本!

标签: c# reflection


【解决方案1】:
  1. 一种方法是:

    type.GetGenericTypeDefinition() == typeof(Nullable<>)
    
  2. 刚刚设置为根据任何其他反射代码:

    propertyInfo.SetValue(yourObject, yourValue);
    

【讨论】:

  • 你的“1”会因为非泛型类型而中断; "Nullable.GetUnderlyingType(type) != null" 更安全。
  • 或者先检查 type.IsGenericTypeDefinition
【解决方案2】:

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

无论哪种方式,对于字符串值,最简单的选择是通过TypeConverter,这在PropertyDescriptor 上更容易(也更准确):

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

这应该使用正确的转换器,即使设置每个属性(而不是每个类型)。最后,如果你做了很多这样的事情,这允许通过HyperDescriptor 进行加速,而无需更改代码(除了为类型启用它,只完成一次)。

【讨论】:

  • 这在将对象与 http 表单值匹配时非常有效,它们都是以字符串开头的。谢谢。
  • @John 实际上我本周在我的博客上在这方面做了更多的坏事 - marcgravell.blogspot.com
【解决方案3】:

专门将整数转换为枚举并分配给可为空的枚举属性:

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);
}

【讨论】:

  • 我建议propertyInfo.SetValue(item, (value == null ? null : enumValue), null);这样可以设置null,否则设置null时,实际上是设置第一个枚举值。
【解决方案4】:

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

编辑:根据 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);
            }
        }

    }
}

【讨论】:

  • Nullable.GetUnderlyingType 会更容易;此外,这不会遵循在属性级别设置的任何 [TypeConverter(...)] 指令,这可能(取决于场景)是一个问题。
  • 好评 Marc,我同意 GetUnderlyingType 会是一个更好的解决方案。
【解决方案5】:

最初,MSDN forum 提到了最佳解决方案。但是,当您需要实现动态解决方案时,您不知道在一个类上可以声明多少个可为空的字段,最好的办法是检查是否可以将 Nullable 类型分配给 属性,您通过反射检查它

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");
            }
            ...
        }
...
}

【讨论】:

    【解决方案6】:

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

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

    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;
        }
    }
    

    注意:当你设置一个可以为空的值(例如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);
        }
    

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

        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);
        
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多