Установить свойство объекта с помощью отражения - PullRequest
293 голосов
/ 06 марта 2009

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

Ex:

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

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

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

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

Ответы [ 10 ]

354 голосов
/ 06 марта 2009

Да, вы можете использовать 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);
}
263 голосов
/ 06 марта 2009

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

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

где target - объект, для которого будет установлено его свойство.

85 голосов
/ 20 января 2012

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

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

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

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

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

25 голосов
/ 16 июля 2013

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

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);
}
14 голосов
/ 20 января 2012

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

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);
12 голосов
/ 06 марта 2009

Вы также можете получить доступ к полям, используя аналогичную манеру:

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

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

11 голосов
/ 24 августа 2015

Используйте что-то вроде этого:

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 голосов
/ 20 августа 2014

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

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;
            }
        }
    }
}
0 голосов
/ 15 февраля 2017

Основываясь на предложении 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);
            }
        }
    }
0 голосов
/ 21 сентября 2016

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

Вот пакет

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

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

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

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

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

LineItem lineItem = new LineItem();

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

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

* * Т.е. 1 022. У класса Invite есть свойство с именем State, у которого есть свойство с именем Invite (типа приглашения), у которого есть свойство с именем Recipient, у которого есть свойство с именем Id.

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

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

Invite invite = new Invite();

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