how to set nullable type via reflection code ( c#)?

后端 未结 8 1335
我寻月下人不归
我寻月下人不归 2020-12-28 16:05

I need to set the properties of a class using reflection.

I have a Dictionary with property names and string values.

Inside

相关标签:
8条回答
  • 2020-12-28 16:21

    I've created small sample. If you have any questions regarding this code, please add comments.

    EDIT: updated sample based on great comment by 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);
                }
            }
    
        }
    }
    
    0 讨论(0)
  • 2020-12-28 16:21

    here is the safest solution for "nullable" object type

    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 讨论(0)
  • 2020-12-28 16:27

    Originally, the best solution is mentioned at MSDN forum. However, when you need to implement dynamic solution, where you don't know exactly how many nullable fields may be declared on a class, you best bet is to check if Nullable<> type can be assigned to the property, which you inspect via reflection

    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 讨论(0)
  • 2020-12-28 16:31

    Checking for Nullable types is easy, int? is actually System.Nullable<System.Int32>. So you just check if the type is a generic instance of System.Nullable<T>. Setting shouldn't make a difference, nullableProperty.SetValue(instance, null) or nullableProperty.SetValue(instance, 3)

    0 讨论(0)
  • 2020-12-28 16:33

    I have used the following solution, avoiding use of type converter for taking more control over code.

    I wrote a helper class for supporting operations

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

    Note: When You set a nullable value (eg. int?, the value need to be almost an integer or castable type. You cannot set int on a byte?. So, You need to convert properly. See ConvertValue() code, that checks either for type (int) and corresponding nullable type (int?) )

    This is code for setting values with the required data structure, Dictionary.

        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);
        }
    
    0 讨论(0)
  • 2020-12-28 16:38

    Specifically to convert an integer to an enum and assign to a nullable enum property:

    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);
    }
    
    0 讨论(0)
提交回复
热议问题