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

时间:2009-01-15 12:21:17

标签: c# reflection

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

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

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

  1. 如果属性是可以为空的类型,我如何从PropertyInfo中知道?
  2. 如何使用反射设置可空类型?
  3. 修改 在这个博客的评论中定义的第一个方法似乎也是这样做的: http://weblogs.asp.net/pjohnson/archive/2006/02/07/437631.aspx

8 个答案:

答案 0 :(得分:13)

  1. 执行此操作的一种方法是:

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

    propertyInfo.SetValue(yourObject, yourValue);
    

答案 1 :(得分:13)

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

无论哪种方式,使用字符串值,最简单的选项是通过TypeConverter PropertyDescriptorPropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj); // then per property... PropertyDescriptor prop = props[propName]; prop.SetValue(obj, prop.Converter.ConvertFromInvariantString(value)); 更容易(更准确)地提供:

{{1}}

这应该使用正确的转换器,即使设置per-property(而不是per-type)。最后,如果您正在执行大量此操作,则可以通过HyperDescriptor进行加速,而无需更改代码(除了为类型启用它之外,仅执行一次)。

答案 2 :(得分:6)

特别是将整数转换为枚举并分配给可以为空的枚举属性:

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

答案 3 :(得分:3)

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

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

    }
}

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

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

Sub Rowcount()
    'Charles, M
    Dim H As Long
    Set sh = Worksheets("Report-Charles, M")


    H = sh.UsedRange.Rows.Count

        Range("I2:I" & H).Formula = "=IF((AND(OR(E2=""Unrated"",E2=""""),OR(G2<>""Unrated"",G2<>""""))),G2,IF(E2>=""4,25"",""High Performance"",IF(E2<""3,35"",""Low Performance"",IF(AND(E2>=""3,35"",E2<""4,25""),""Medium Performance"",""Unrated""))))"

    MsgBox (H) & "Rows have been Autofilled with 3 scale Rating Results"
End Sub

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

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

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

答案 6 :(得分:0)

检查Nullable类型很容易,int?实际上是System.Nullable<System.Int32>。 因此,您只需检查类型是否为System.Nullable<T>的通用实例。 设置不应有所作为,nullableProperty.SetValue(instance, null)nullableProperty.SetValue(instance, 3)

答案 7 :(得分:0)

这是&#34; nullable&#34;最安全的解决方案。对象类型

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

    }