Установить свойство объекта с помощью отражения

Есть ли способ в С#, где я могу использовать отражение, чтобы установить свойство объекта?

Пример:

MyObject obj = new MyObject();
obj.Name = "Value";

Я хочу установить obj.Name с отражением. Что-то вроде:

Reflection.SetProperty(obj, "Name") = "Value";

Есть ли способ сделать это?

Ответ 1

Да, вы можете использовать Type.InvokeMember():

using System.Reflection;
MyObject obj = new MyObject();
obj.GetType().InvokeMember("Name",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
    Type.DefaultBinder, obj, "Value");

Это вызовет исключение, если obj не имеет свойства, называемого Name, или оно не может быть установлено.

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

using System.Reflection;
MyObject obj = new MyObject();
PropertyInfo prop = obj.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
if(null != prop && prop.CanWrite)
{
    prop.SetValue(obj, "Value", null);
}

Ответ 2

Вы также можете сделать:

Type type = target.GetType();

PropertyInfo prop = type.GetProperty("propertyName");

prop.SetValue (target, propertyValue, null);

где target - объект, который будет иметь свой набор свойств.

Ответ 3

Отражение, в основном, т.е.

myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);

или есть библиотеки, которые помогут как с точки зрения удобства, так и производительности; например FastMember:

var wrapped = ObjectAccessor.Create(obj); 
wrapped[property] = "Bob";

(что также имеет то преимущество, что не нужно заранее знать, является ли это полем по отношению к свойству)

Ответ 4

Или вы можете перенести Marc один лайнер в свой собственный класс расширения:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object obj, string propName, object value)
    {
        obj.GetType().GetProperty(propName).SetValue(obj, value, null);
    }
}

и назовите его следующим образом:

myObject.SetPropertyValue("myProperty", "myValue");

Для хорошей меры добавьте метод для получения значения свойства:

public static object GetPropertyValue(this object obj, string propName)
{
        return obj.GetType().GetProperty(propName).GetValue (obj, null);
}

Ответ 5

Да, используя System.Reflection:

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);

Ответ 6

Вы также можете получить доступ к полям с помощью simillar:

var obj=new MyObject();
FieldInfo fi = obj.GetType().
  GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)

С отражением все может быть открытой книгой:) В моем примере мы привязываемся к полю частного уровня экземпляра.

Ответ 7

Используйте такие вещи, как это:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    property.SetValue(p_object, Convert.ChangeType(value, property.PropertyType), null);
   }
}

или

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(p_object, safeValue, null);
   }
}

Ответ 8

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

public static void Assign(this object destination, object source)
    {
        if (destination is IEnumerable && source is IEnumerable)
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while (dest_enumerator.MoveNext() && src_enumerator.MoveNext())
                dest_enumerator.Current.Assign(src_enumerator.Current);
        }
        else
        {
            var destProperties = destination.GetType().GetProperties();
            foreach (var sourceProperty in source.GetType().GetProperties())
            {
                foreach (var destProperty in destProperties)
                {
                    if (destProperty.Name == sourceProperty.Name && destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        destProperty.SetValue(destination,     sourceProperty.GetValue(source, new object[] { }), new object[] { });
                        break;
            }
        }
    }
}

Ответ 9

Я только что опубликовал пакет Nuget, который позволяет настраивать не только свойства первого уровня, но и вложенные свойства в заданном объекте на любой глубине.

Вот пакет

Устанавливает значение свойства объекта по его пути из корня.

Объект может быть сложным объектом, а свойство может быть многоуровневым вложенным свойством или может быть свойством непосредственно под корнем. ObjectWriter найдет свойство, используя параметр пути свойства и обновит его значение. Путь к свойствам - это добавленные имена свойств, посещенных от корневого до конца свойства node, который мы хотим установить, ограниченным параметром строки разделителя.

Использование:

Для настройки свойств непосредственно под корнем объекта:

Т.е. LineItem класс имеет свойство int, называемое ItemId

LineItem lineItem = new LineItem();

ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);

Для установки вложенного свойства несколько уровней ниже корня объекта:

Т.е. Invite class имеет свойство State, которое имеет свойство Invite (типа приглашения), которое имеет свойство Recipient, которое имеет свойство Id.

Чтобы сделать вещи еще более сложными, свойство State не является ссылочным типом, это struct.

Вот как вы можете установить свойство Id (в строковое значение "outlook" ) внизу дерева объектов в одной строке.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");

Ответ 10

Основываясь на предложении MarcGravell, я построил следующий статический метод. Метод в общем присваивает всем подходящим свойствам исходного объекта цели, используя FastMember

 public static void DynamicPropertySet(object source, object target)
    {
        //SOURCE
        var src_accessor = TypeAccessor.Create(source.GetType());
        if (src_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var src_members = src_accessor.GetMembers();
        if (src_members == null)
        {
            throw new ApplicationException("Could not fetch members!");
        }
        var src_class_members = src_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var src_class_propNames = src_class_members.Select(x => x.Name);
        var src_propNames = src_members.Except(src_class_members).Select(x => x.Name);

        //TARGET
        var trg_accessor = TypeAccessor.Create(target.GetType());
        if (trg_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_members = trg_accessor.GetMembers();
        if (trg_members == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_class_members = trg_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var trg_class_propNames = trg_class_members.Select(x => x.Name);
        var trg_propNames = trg_members.Except(trg_class_members).Select(x => x.Name);



        var class_propNames = trg_class_propNames.Intersect(src_class_propNames);
        var propNames = trg_propNames.Intersect(src_propNames);

        foreach (var propName in propNames)
        {
            trg_accessor[target, propName] = src_accessor[source, propName];
        }
        foreach (var member in class_propNames)
        {
            var src = src_accessor[source, member];
            var trg = trg_accessor[target, member];
            if (src != null && trg != null)
            {
                DynamicPropertySet(src, trg);
            }
        }
    }