Legen Sie die Objekteigenschaft mithilfe der Reflexion fest

323

Gibt es in C # eine Möglichkeit, mithilfe von Reflektion eine Objekteigenschaft festzulegen?

Ex:

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

Ich möchte obj.Namemit Reflexion setzen. Etwas wie:

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

Gibt es eine Möglichkeit, dies zu tun?

Melursus
quelle

Antworten:

391

Ja, Sie können verwenden Type.InvokeMember():

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

Dies objlöst eine Ausnahme aus, wenn keine Eigenschaft aufgerufen Namewurde oder nicht festgelegt werden kann.

Ein anderer Ansatz besteht darin, die Metadaten für die Eigenschaft abzurufen und dann festzulegen. Auf diese Weise können Sie überprüfen, ob die Eigenschaft vorhanden ist, und sicherstellen, dass sie festgelegt werden kann:

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);
}
Andy
quelle
73
Wenn Sie nicht mit allen Zeichenfolgen arbeiten, möchten Sie möglicherweise zuerst die Daten konvertieren: var val = Convert.ChangeType(propValue, propInfo.PropertyType); source: devx.com/vb2themax/Tip/19599
LostNomad311
4
Alternativ können Sieobj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield
1
Es ist unmöglich, einen Wert für CanWrite=FalseTypen festzulegen, oder?
T.Todua
287

Sie können auch:

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

Dabei ist Ziel das Objekt, dessen Eigenschaft festgelegt wird.

El Cheicon
quelle
11
Ich habe heute genau das Gleiche getan. Das obige funktioniert gut, offensichtlich sollte eine Nullprüfung an der Requisite durchgeführt werden, bevor versucht wird, sie zu verwenden.
Antony Scott
3
@AntonyScott Ich würde denken, Sie möchten wissen, ob Sie die falsche Eigenschaft aufrufen, also scheint "lautlos scheitern" ein schlechter Kurs zu sein.
jih
3
@jih Ich kann Ihren Standpunkt sehen, aber es hängt wirklich von der Situation ab.
Antony Scott
94

Reflexion im Grunde, dh

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

oder es gibt Bibliotheken, die sowohl hinsichtlich der Benutzerfreundlichkeit als auch der Leistung helfen. Zum Beispiel mit FastMember :

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

(was auch den Vorteil hat, dass man nicht im Voraus wissen muss, ob es sich um ein Feld gegen eine Eigenschaft handelt)

Marc Gravell
quelle
Wow, ich war ein wenig verwirrt von der Zusammenführung, aber ich habe deine Antwort wiedergefunden! Vielen Dank, Sie verdienen ein "Akzeptieren", aber da mein Thread zusammengeführt wird :(
Nochmals vielen
@MarcGravell, ich habe mir FastMember angesehen und es ist ziemlich interessant. Gibt es irgendwo einen Einstieg / ein Tutorial für uns Sterbliche, um diese großartige Bibliothek von euch zu benutzen?
Sudhanshu Mishra
Wie kann ich die Art der Immobilie von FastMember erhalten?
Sagte Roohullah Allem
@Jahan accessor => GetMembers => Member => Typ
Marc Gravell
gute Antwort! Das Gute an einem Oneliner ist, dass es viel schneller zu verstehen ist, da es keine benutzernamen Variablen gibt, zwischen denen für Sie möglicherweise kein Sinn ergibt.
Bastiaan
27

Oder Sie können Marc's einen Liner in Ihre eigene Erweiterungsklasse einwickeln:

public static class PropertyExtension{       

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

und nenne es so:

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

Fügen Sie für eine gute Maßnahme eine Methode hinzu, um einen Eigenschaftswert zu erhalten:

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

Ja, mit System.Reflection:

using System.Reflection;

...

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

Verwenden Sie Folgendes:

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

oder

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);
   }
}
Ardalan Shahgholi
quelle
2
Der Teil, in dem Sie den Eigenschaftstyp erhalten und dann umwandeln, war für mich wirklich nützlich. Es wirkt wie ein Zauber. Vielen Dank
Marc
12

Sie können auch auf ähnliche Weise auf Felder zugreifen:

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

Mit Reflexion kann alles ein offenes Buch sein :) In meinem Beispiel binden wir an ein Feld auf privater Instanzebene.

JoshBerke
quelle
8

Sie können dies ausprobieren, wenn Sie Eigenschaften eines Objekts von einem anderen Objekt mithilfe von Eigenschaftsnamen massenweise zuweisen möchten:

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
quelle
2
Hallo und willkommen bei Stack Overflow. Bitte formatieren Sie Ihren Code richtig und legen Sie ihn nicht nur in Ihrem Beitrag ab, er hilft anderen, Ihre Antwort zu verstehen.
ThreeFx
0

Ich habe gerade ein Nuget-Paket veröffentlicht, mit dem nicht nur die Eigenschaften der ersten Ebene, sondern auch verschachtelte Eigenschaften im angegebenen Objekt in beliebiger Tiefe eingerichtet werden können.

Hier ist das Paket

Legt den Wert einer Eigenschaft eines Objekts anhand seines Pfads vom Stamm fest.

Das Objekt kann ein komplexes Objekt sein und die Eigenschaft kann eine tief verschachtelte Eigenschaft mit mehreren Ebenen sein oder es kann eine Eigenschaft direkt unter dem Stamm sein. ObjectWriterfindet die Eigenschaft mithilfe des Eigenschaftspfadparameters und aktualisiert ihren Wert. Der Eigenschaftspfad sind die angehängten Namen der Eigenschaften, die vom Stamm bis zur Endknoteneigenschaft besucht werden, die wir festlegen möchten, getrennt durch den Trennzeichenfolgenparameter.

Verwendungszweck:

So richten Sie die Eigenschaften direkt unter dem Objektstamm ein:

Dh. LineItemKlasse hat eine int-Eigenschaft namensItemId

LineItem lineItem = new LineItem();

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

Zum Einrichten verschachtelter Eigenschaften auf mehreren Ebenen unterhalb des Objektstamms:

Dh. InviteKlasse hat eine Eigenschaft namens State, die eine Eigenschaft namens hatInvite (vom Typ Invite) hat, die eine Eigenschaft namens hat Recipient, die eine Eigenschaft namens hatId .

Um die Dinge noch komplexer zu machen, ist die State Eigenschaft kein Referenztyp, sondern ein struct.

So können Sie die Id-Eigenschaft (auf den Zeichenfolgenwert von "Outlook") am unteren Rand des Objektbaums in einer einzelnen Zeile festlegen.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");
Dogu Arslan
quelle
0

Basierend auf MarcGravells Vorschlag habe ich die folgende statische Methode erstellt. Die Methode weist mithilfe von FastMember generell alle übereinstimmenden Eigenschaften vom Quellobjekt zum Ziel zu

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