Imposta la proprietà dell'oggetto usando la riflessione


323

Esiste un modo in C # in cui posso usare la riflessione per impostare una proprietà dell'oggetto?

Ex:

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

Voglio impostare obj.Namecon la riflessione. Qualcosa di simile a:

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

C'è un modo per farlo?

Risposte:


391

Sì, puoi usare Type.InvokeMember():

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

Ciò genererà un'eccezione se objnon è stata chiamata una proprietà Nameo non può essere impostata.

Un altro approccio è quello di ottenere i metadati per la proprietà e quindi impostarli. Ciò ti consentirà di verificare l'esistenza della proprietà e di verificare che possa essere impostata:

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
Se non hai a che fare con tutte le stringhe potresti voler prima convertire i dati: var val = Convert.ChangeType(propValue, propInfo.PropertyType); fonte: devx.com/vb2themax/Tip/19599
LostNomad311

4
in alternativa, puoi utilizzareobj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield

1
è impossibile impostare un valore per i CanWrite=Falsetipi, giusto?
T.Todua,

287

Puoi anche fare:

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

dove target è l'oggetto che avrà la sua proprietà impostata.


11
Oggi ho fatto la stessa cosa. Quanto sopra funziona alla grande, ovviamente un controllo nullo dovrebbe essere fatto su prop prima di tentare di usarlo.
Antony Scott,

3
@AntonyScott Penserei che vorresti sapere se stai invocando la proprietà sbagliata, quindi "fallire in silenzio" sembra un brutto corso.
jih

3
@jih posso vedere il tuo punto, ma dipende davvero dalla situazione.
Antony Scott,

94

Riflessione, in sostanza, cioè

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

o ci sono librerie per aiutare sia in termini di convenienza che di prestazioni; ad esempio con FastMember :

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

(che ha anche il vantaggio di non aver bisogno di sapere in anticipo se si tratta di un campo rispetto a una proprietà)


Caspita, mi sono un po 'confuso dalla fusione, ma ho trovato di nuovo la tua risposta! Grazie, ti meriti un 'accetta', ma dal momento che il mio thread è unito :( Grazie ancora!
halfpastfour.am

@MarcGravell, stavo guardando FastMember ed è piuttosto interessante. C'è un tutorial / tutorial da qualche parte per noi semplici mortali per usare questa tua grande lib?
Sudhanshu Mishra,

Come posso ottenere il tipo di proprietà da FastMember?
Disse Roohullah Allem il

@Jahan accessor => GetMembers => Member => Type
Marc Gravell

Bella risposta! La cosa buona di un oneliner è che è molto più veloce da capire, dal momento che non ci sono variabili nominate dall'utente tra le quali per te potrebbe non avere alcun senso ..
Bastiaan

27

Oppure potresti avvolgere un solo liner di Marc nella tua classe di estensione:

public static class PropertyExtension{       

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

e chiamalo così:

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

Per una buona misura, aggiungiamo un metodo per ottenere un valore di proprietà:

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

14

Sì, usando System.Reflection:

using System.Reflection;

...

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

13

Usa qualcosa del genere:

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

o

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
La parte in cui ottieni il tipo di proprietà e poi lo lancia è stata davvero utile per me. Esso funziona magicamente. Grazie
Marc

12

Puoi anche accedere ai campi usando una modalità simile:

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

Con la riflessione tutto può essere un libro aperto :) Nel mio esempio siamo vincolanti a un campo a livello di istanza privato.


8

Puoi provarlo quando vuoi assegnare in massa le proprietà di un oggetto da un altro oggetto usando i nomi delle proprietà:

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
Ciao e benvenuto su Stack Overflow. Per favore formatta il tuo codice correttamente e non scaricarlo semplicemente nel tuo post, aiuterà gli altri a capire la tua risposta.
ThreeFx,

0

Ho appena pubblicato un pacchetto Nuget che consente di impostare non solo le proprietà di primo livello ma anche le proprietà nidificate nell'oggetto specificato in qualsiasi profondità.

Ecco il pacchetto

Imposta il valore di una proprietà di un oggetto in base al suo percorso dalla radice.

L'oggetto può essere un oggetto complesso e la proprietà può essere una proprietà nidificata profonda a più livelli o può essere una proprietà direttamente sotto la radice. ObjectWritertroverà la proprietà usando il parametro path della proprietà e aggiornerà il suo valore. Il percorso della proprietà è il nome aggiunto delle proprietà visitate dalla radice alla proprietà del nodo finale che vogliamo impostare, delimitata dal parametro stringa delimitatore.

Uso:

Per impostare le proprietà direttamente sotto la radice dell'oggetto:

Vale a dire. LineItemla classe ha una proprietà int chiamataItemId

LineItem lineItem = new LineItem();

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

Per impostare la proprietà nidificata su più livelli sotto la radice dell'oggetto:

Vale a dire. InviteLa classe ha una proprietà chiamata State, che ha una proprietà chiamata Invite(di tipo Invita), che ha una proprietà chiamata Recipient, che ha una proprietà chiamata Id.

Per rendere le cose ancora più complesse, la Stateproprietà non è un tipo di riferimento, è a struct.

Ecco come è possibile impostare la proprietà Id (sul valore stringa di "Outlook") nella parte inferiore dell'albero degli oggetti in una singola riga.

Invite invite = new Invite();

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

0

Sulla base del suggerimento di MarcGravell, ho costruito il seguente metodo statico: il metodo assegna genericamente tutte le proprietà corrispondenti dall'oggetto di origine alla destinazione utilizzando 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);
            }
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.