Как установить обнуляемый тип с помощью кода отражения (C #)? - PullRequest
21 голосов
/ 15 января 2009

Мне нужно установить свойства класса, используя отражение.

У меня есть Dictionary<string,string> с именами свойств и строковыми значениями.

Внутри цикла отражения мне нужно преобразовать строковое значение в соответствующий тип свойства при настройке значения для каждого свойства. Некоторые из этих типов свойств могут быть обнуляемыми.

  1. Как я могу узнать из PropertyInfo, является ли свойство обнуляемым типом?
  2. Как я могу установить обнуляемый тип, используя отражение?

редактирование: Первый метод, определенный в комментариях к этому блогу, похоже, тоже помогает: http://weblogs.asp.net/pjohnson/archive/2006/02/07/437631.aspx

Ответы [ 8 ]

14 голосов
/ 15 января 2009
  1. Один из способов сделать это:

    type.GetGenericTypeDefinition() == typeof(Nullable<>)
    
  2. Просто установите как для любого другого кода отражения:

    propertyInfo.SetValue(yourObject, yourValue);
    
13 голосов
/ 15 января 2009

Зачем вам нужно знать, является ли оно обнуляемым? И вы имеете в виду «ссылочный тип» или «Nullable<T>»?

В любом случае, со строковыми значениями, самый простой вариант будет через TypeConverter, который легче (и точнее) доступен в PropertyDescriptor:

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

При этом следует использовать правильный конвертер, даже если он установлен для каждого свойства (а не для каждого типа). Наконец, если вы делаете это много, это позволяет ускорение с помощью HyperDescriptor, без изменения кода (кроме включения его для типа, выполняется только один раз).

6 голосов
/ 19 октября 2010

В частности, чтобы преобразовать целое число в перечисление и присвоить свойству enullable перечисление:

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 голосов
/ 15 января 2009

Я создал небольшой образец. Если у вас есть какие-либо вопросы относительно этого кода, пожалуйста, добавьте комментарии.

РЕДАКТИРОВАТЬ : обновленный образец, основанный на великолепном комментарии Марка Гравелла

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

    }
}
1 голос
/ 16 мая 2016

Я использовал следующее решение, избегая использования преобразователя типов для получения большего контроля над кодом.

Я написал вспомогательный класс для поддержки операций

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

Примечание. Когда вы устанавливаете значение, допускающее значение NULL (например, 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);
    }
1 голос
/ 07 сентября 2011

Первоначально лучшее решение упоминается на форуме MSDN . Однако, когда вам нужно реализовать динамическое решение, когда вы точно не знаете, сколько полей, которые могут быть объявлены как NULL, могут быть объявлены в классе, лучше всего проверить, можно ли присвоить типу 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");
            }
            ...
        }
...
}
0 голосов
/ 28 ноября 2015

вот самое безопасное решение для типа объекта "nullable"

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

    }
0 голосов
/ 15 января 2009

Проверка на Nullable типы проста, не так ли? на самом деле System.Nullable<System.Int32>. Таким образом, вы просто проверяете, является ли тип универсальным экземпляром System.Nullable<T>. Настройка не должна иметь значения, nullableProperty.SetValue(instance, null) или nullableProperty.SetValue(instance, 3)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...