Ustaw właściwość obiektu za pomocą odbicia


323

Czy istnieje sposób w języku C #, w którym można użyć odbicia do ustawienia właściwości obiektu?

Dawny:

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

Chcę ustawić obj.Namez refleksją. Coś jak:

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

Czy jest na to sposób?

Odpowiedzi:


391

Tak, możesz użyć Type.InvokeMember():

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

Spowoduje to zgłoszenie wyjątku, jeśli objnie ma właściwości o nazwie Namelub nie można jej ustawić.

Innym podejściem jest uzyskanie metadanych dla właściwości, a następnie ustawienie jej. Umożliwi to sprawdzenie istnienia właściwości i sprawdzenie, czy można ją ustawić:

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
Jeśli nie masz do czynienia ze wszystkimi ciągami znaków, możesz najpierw przekonwertować dane: var val = Convert.ChangeType(propValue, propInfo.PropertyType); źródło: devx.com/vb2themax/Tip/19599
LostNomad311

4
alternatywnie możesz użyćobj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield

1
nie można ustawić wartości dla CanWrite=Falsetypów, prawda?
T.Todua

287

Możesz także:

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

gdzie cel to obiekt, dla którego zostanie ustawiona jego właściwość.


11
Zrobiłem dzisiaj to samo. Powyższe działa świetnie, oczywiście przed próbą użycia na rekwizytach należy wykonać zerowanie.
Antony Scott

3
@AntonyScott Myślę, że chciałbyś wiedzieć, czy powołujesz się na niewłaściwą właściwość, więc „niepowodzenie w ciszy” wydaje się złym kursem.
dżih.

3
@jih Rozumiem twój punkt widzenia, ale tak naprawdę zależy to od sytuacji.
Antony Scott

94

Zasadniczo odbicie, tj

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

lub istnieją biblioteki, które pomogą zarówno pod względem wygody, jak i wydajności; na przykład w FastMember :

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

(co ma tę zaletę, że nie trzeba z góry wiedzieć, czy jest to pole czy nieruchomość)


Wow, trochę się pogubiłem po fuzji, ale znalazłem twoją odpowiedź ponownie! Dziękuję, zasługujesz na „zaakceptuj”, ale ponieważ mój wątek został scalony :(
Jeszcze

@MarcGravell, patrzyłem na FastMember i jest to dość interesujące. Czy jest gdzieś dla nas wprowadzenie / samouczek dla zwykłych śmiertelników, aby korzystać z waszej wspaniałej biblioteki?
Sudhanshu Mishra,

Jak mogę uzyskać typ właściwości przez FastMember?
powiedział Roohullah Allem

@Jahan accessor => GetMembers => Członek => Typ
Marc Gravell

świetna odpowiedź! Zaletą programu oneliner jest to, że jest on znacznie szybszy do zrozumienia, ponieważ nie ma żadnych zmiennych o nazwach użytkownika, pomiędzy którymi może nie mieć żadnego sensu.
Bastiaan

27

Lub możesz owinąć jedną wkładkę Marca we własną klasę rozszerzenia:

public static class PropertyExtension{       

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

i nazwij to tak:

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

Dla dokładności dodajmy metodę uzyskania wartości właściwości:

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

14

Tak, używając System.Reflection:

using System.Reflection;

...

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

13

Użyj czegoś takiego:

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

lub

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
Część, w której dostajesz typ właściwości, a następnie ją rzutujesz, była dla mnie bardzo przydatna. To działa jak urok. Dziękuję
Marc

12

Możesz również uzyskać dostęp do pól w podobny sposób:

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

Dzięki refleksji wszystko może być otwartą książką :) W moim przykładzie wiążemy się z polem na poziomie instancji prywatnej.


8

Możesz to wypróbować, jeśli chcesz przypisać właściwości obiektu do innego obiektu za pomocą nazw właściwości:

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
Cześć i witamy w Stack Overflow. Sformatuj kod poprawnie, a nie tylko zrzuć go w swoim poście, pomoże to innym zrozumieć twoją odpowiedź.
ThreeFx,

0

Właśnie opublikowałem pakiet Nuget, który pozwala ustawić nie tylko właściwości pierwszego poziomu, ale także zagnieżdżone właściwości w danym obiekcie na dowolnej głębokości.

Oto paczka

Ustawia wartość właściwości obiektu na podstawie ścieżki od katalogu głównego.

Obiekt może być obiektem złożonym, a właściwość może być wielopoziomową właściwością zagnieżdżoną głęboko lub może być właściwością bezpośrednio pod katalogiem głównym. ObjectWriterznajdzie właściwość za pomocą parametru ścieżki właściwości i zaktualizuje jej wartość. Ścieżka właściwości to dołączone nazwy właściwości odwiedzanych od katalogu głównego do właściwości węzła końcowego, które chcemy ustawić, rozdzielone parametrem ciągu separatora.

Stosowanie:

Aby skonfigurować właściwości bezpośrednio pod katalogiem głównym obiektu:

To znaczy. LineItemklasa ma właściwość int o nazwieItemId

LineItem lineItem = new LineItem();

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

Aby skonfigurować zagnieżdżoną właściwość na wielu poziomach poniżej katalogu głównego obiektu:

To znaczy. Inviteklasa ma właściwość o nazwie State, która ma właściwość o nazwie Invite(typu Invite), która ma właściwość o nazwie Recipient, która ma właściwość o nazwie Id.

Aby uczynić rzeczy jeszcze bardziej złożonymi, Statewłaściwość nie jest typem referencyjnym, jest to struct.

Oto jak możesz ustawić właściwość Id (na wartość ciągu „outlook”) na dole drzewa obiektów w jednym wierszu.

Invite invite = new Invite();

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

0

W oparciu o sugestię MarcGravell zbudowałem następującą metodę statyczną. Metoda ogólnie przypisuje wszystkie pasujące właściwości obiektu źródłowego do obiektu docelowego za pomocą 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);
            }
        }
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.