2009-03-06 6 views
243

C'è un modo in C# 3.5 in cui posso utilizzare la riflessione per impostare una proprietà dell'oggetto?Imposta la proprietà dell'oggetto utilizzando la riflessione

Es:

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

voglio impostare obj.Name con la riflessione. Qualcosa di simile:

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

C'è un modo per farlo?

risposta

297

Sì, è possibile utilizzare Type.InvokeMember():

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

Questo sarà un'eccezione se obj non ha una proprietà chiamata Name, o non può essere impostato.

Un altro approccio è ottenere i metadati per la proprietà e quindi impostarlo. Questo vi permetterà di verificare l'esistenza della proprietà, e verificare che può essere impostato:

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

Se non si tratta con tutte le stringhe potresti voler convertire prima i dati: 'var val = Convert.ChangeType (propValue, propInfo.PropertyType);' fonte: http://www.devx.com/vb2themax/Tip/19599 – LostNomad311

+2

in alternativa, yo puoi usare 'obj.GetType(). GetProperty (" Name ")?. GetSetMethod() ?. Invoke (...)' – tecfield

227

Si può anche fare:

Type type = target.GetType(); 

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

prop.SetValue (target, propertyValue, null); 

cui obiettivo è l'oggetto che avrà la sua proprietà impostato.

+10

Ho fatto la stessa cosa oggi. Quanto sopra funziona alla grande, ovviamente un controllo nullo dovrebbe essere fatto sul puntello prima di tentare di usarlo. –

+4

+1 per codice più pulito !!! – Sham

+3

@AntonyScott Direi che vorresti sapere se stai invocando la proprietà sbagliata, quindi "fallire silenziosamente" sembra una cattiva strada. –

11

si può anche accedere campi utilizzando maniera simillar:

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

Con riflessione Tutto può essere un libro aperto :) Nel mio esempio stiamo legando ad un campo livello di istanza privato.

76

Riflessione, in pratica, vale a dire.

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

o ci sono biblioteche per aiutare sia in termini di convenienza e prestazioni; per 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 contro una proprietà)

+0

Wow, mi sono un po 'confuso dall'unione, ma ho trovato di nuovo la tua risposta! Grazie, ti meriti un 'accept', ma dal momento che il mio thread è unito :(Grazie ancora! –

+0

@Bob scusa per qualsiasi confusione –

+0

+1 per la soluzione a linea singola. Funzionava come un incantesimo. Grazie Marc. – pordi

13

Sì, utilizzando System.Reflection:

using System.Reflection; 

... 

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

Oppure puoi avvolgere il rivestimento di Marc all'interno della 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 chiamare in questo modo:

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

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

Si può provare questo fuori quando si desidera proprietà di massa-assegnazione di un oggetto da un altro oggetto utilizzando nomi di 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 a Stack Overflow. formatta il tuo codice in modo corretto e non lo scarichi semplicemente nel tuo post, questo aiuterà gli altri a capire la tua risposta – ThreeFx

8

Utilizzare somethings come questo:

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

La parte in cui ottieni il tipo di proprietà e poi la cast è stata davvero utile per me. Esso funziona magicamente. Grazie – Marc

0

ho appena pubblicato un pacchetto Nuget che permette la creazione non solo la prima proprietà di livello, ma anche proprietà nidificati l'oggetto dato in modo approfondito.

Ecco the package

imposta il valore di una proprietà di un oggetto dal suo percorso dalla radice.

L'oggetto può essere un oggetto complesso e la proprietà può essere una proprietà nidificata a più livelli o può essere una proprietà direttamente sotto la radice. ObjectWriter troverà la proprietà utilizzando il parametro del percorso 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, delimitato dal parametro stringa delimitatore.

utilizzati:

Per impostare le proprietà direttamente sotto la radice oggetto:

Ie. LineItem classe ha una proprietà int chiamato ItemId

LineItem lineItem = new LineItem(); 

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

Per la configurazione di proprietà annidata più livelli sotto la radice oggetto:

Ie. La classe Invite ha una proprietà chiamata State, che ha una proprietà chiamata Invite (di tipo Invite), che ha una proprietà chiamata Recipient, che ha una proprietà chiamata Id.

Per rendere le cose ancora più complesse, la proprietà State non è un tipo di riferimento, è un 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

Basato sul suggerimento di MarcGravell, ho costruito il seguente metodo statico method.The genericamente assegna tutte le proprietà corrispondenti da oggetto di origine di 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); 
      } 
     } 
    }