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

323

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

Пример:

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

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

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

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

Melursus
источник

Ответы:

391

Да, вы можете использовать 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);
}
Энди
источник
73
Если вы не имеете дело со всеми строками, вы можете сначала преобразовать данные: var val = Convert.ChangeType(propValue, propInfo.PropertyType); source: devx.com/vb2themax/Tip/19599
LostNomad311
4
В качестве альтернативы, вы можете использоватьobj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
Tecfield
1
невозможно установить значение для CanWrite=Falseтипов, верно?
Т.Тодуа
287

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

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

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

Эль Шейкон
источник
11
Я сделал то же самое сегодня. Вышеописанное прекрасно работает, очевидно, перед тем, как использовать его, следует проверить нулевую проверку пропеллера.
Энтони Скотт
3
@AntonyScott Я бы подумал, что вы захотите узнать, ссылаетесь ли вы на неправильное свойство, поэтому «молчаливая ошибка» кажется плохим ходом.
Jih
3
@jih Я понимаю твою точку зрения, но это действительно зависит от ситуации.
Энтони Скотт
94

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

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

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

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

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

Марк Гравелл
источник
Ничего себе, немного смутился от слияния, но я снова нашел ваш ответ! Спасибо, вы заслуживаете «принять», но поскольку моя ветка объединена :(
Еще
@MarcGravell, я смотрел на FastMember, и это довольно интересно. Есть ли у нас, простых смертных, начальное / учебное пособие для использования этой вашей замечательной библиотеки?
Судханшу Мишра
Как я могу получить тип недвижимости от FastMember?
сказал Рухулла Аллем
@Jahan accessor => GetMembers => Member => Type
Марк Грэвелл
отличный ответ! Хорошая вещь о oneliner состоит в том, что это намного быстрее понять, так как между ними нет именованных переменных, которые сами по себе могут не иметь никакого смысла ..
Bastiaan
27

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

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

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

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);
Д Стэнли
источник
13

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

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);
   }
}
Ардалан Шахголи
источник
2
Часть, где вы получаете тип свойства, а затем разыгрываете его, была для меня очень полезна. Работает как часы. Спасибо
Марк
12

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

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

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

JoshBerke
источник
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;
            }
        }
    }
}
user3679106
источник
2
Привет и добро пожаловать в Stack Overflow. Пожалуйста, отформатируйте ваш код правильно, а не просто добавьте его в ваш пост, это поможет другим понять ваш ответ.
ThreeFx
0

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

Вот посылка

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

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

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

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

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

LineItem lineItem = new LineItem();

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

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

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

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

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

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");
Догу Арслан
источник
0

Основываясь на предложении 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);
            }
        }
    }
убедительный
источник