Встановіть властивість об'єкта за допомогою відображення


323

Чи є спосіб на C #, де я можу використовувати відображення для встановлення властивості об'єкта?

Наприклад:

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

Я хочу встановити obj.Nameрефлексію. Щось на зразок:

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

Чи є спосіб зробити це?

Відповіді:


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); джерело: 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 Я б подумав, що ви хочете знати, якщо ви посилаєтесь на неправильну власність, тому "мовчки не вдається" здається поганим курсом.
jim

3
@jih Я бачу вашу думку, але це залежить від ситуації.
Ентоні Скотт

94

Рефлексія, в основному, тобто

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

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

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

(що також має перевагу в тому, що не потрібно заздалегідь знати, чи це поле проти властивості)


Вау, трохи заплутався від злиття, але я знову знайшов вашу відповідь! Дякую, ти заслуговуєш на "прийняття", але оскільки моя нитка об'єднана :( Дякую ще раз!
halfpastfour.am

@MarcGravell, я дивився на FastMember, і це досить цікаво. Чи є десь для нас простого смертного початок / підручник для використання цієї великої вашої лайки?
Sudhanshu Mishra

Як я можу отримати тип власності FastMember?
Саїд Рохулла Аллем

@Jahan accessor => GetMembers => Член => Тип
Марк Гравелл

чудова відповідь! Хороша річ в онлайнері полягає в тому, що це набагато швидше зрозуміти, оскільки немає змінних між користувачем змінних, між якими для себе може не бути сенсу ..
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)

З відображенням все може бути відкритою книгою :) У моєму прикладі ми прив'язуємося до поля рівня приватного примірника.


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

2
Привіт, ласкаво просимо до Stack Overflow. Будь ласка, форматуйте свій код належним чином, а не просто скидайте його у свій пост, це допоможе іншим зрозуміти вашу відповідь.
ThreeFx

0

Я щойно опублікував пакет Nuget, який дозволяє налаштувати не тільки властивості першого рівня, але і вкладені властивості в заданому об'єкті в будь-якій глибині.

Ось пакет

Встановлює значення властивості об'єкта шляхом його шляху від кореня.

Об'єкт може бути складним об'єктом, а властивість може бути багаторівневою глибоко вкладеною властивістю або може бути властивістю безпосередньо під коренем. ObjectWriterзнайде властивість за допомогою параметра шляху властивості та оновить його значення. Шлях до властивостей - це додані назви властивостей, відвіданих від root до властивості кінцевого вузла, які ми хочемо встановити, обмежених параметром рядка роздільника.

Використання:

Для налаштування властивостей безпосередньо під корінь об'єкта:

Тобто 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);
            }
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.