Come faccio a scorrere le proprietà di un object anonimo in C #?

Voglio prendere un object anonimo come argomento per un metodo, e quindi scorrere le sue proprietà per aggiungere ogni proprietà / valore ad un ExpandoObject dinamico.

Quindi quello di cui ho bisogno è di andare

 new { Prop1 = "first value", Prop2 = SomeObjectInstance, Prop3 = 1234 } 

conoscere i nomi e i valori di ogni proprietà e poterli aggiungere a ExpandoObject .

Come posso realizzare questo?

Nota a margine: questo sarà fatto in molti dei miei test unitari (lo sto usando per rifattorare molta roba nel setup), quindi le prestazioni sono in qualche misura rilevanti. Non ne so abbastanza del riflesso da dire con certezza, ma da quello che ho capito è piuttosto pesante, quindi se è ansible preferirei evitarlo …

Domanda successiva: Come ho detto, sto prendendo questo object anonimo come argomento per un metodo. Quale tipo di dati dovrei usare nella firma del metodo? Tutte le proprietà saranno disponibili se utilizzo l’ object ?

 foreach(var prop in myVar.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { Console.WriteLine("Name: {0}, Value: {1}",prop.Name, prop.GetValue(myVar,null)); } 

Rifletti sull’object anonimo per ottenere i suoi nomi e valori di proprietà, quindi approfitta di un ExpandoObject che in realtà è un dizionario per popolarlo. Ecco un esempio, express come test unitario:

  [TestMethod] public void ShouldBeAbleToConvertAnAnonymousObjectToAnExpandoObject() { var additionalViewData = new {id = "myControlId", css = "hide well"}; dynamic result = new ExpandoObject(); var dict = (IDictionary)result; foreach (PropertyInfo propertyInfo in additionalViewData.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { dict[propertyInfo.Name] = propertyInfo.GetValue(additionalViewData, null); } Assert.AreEqual(result.id, "myControlId"); Assert.AreEqual(result.css, "hide well"); } 

Un approccio alternativo consiste nell’utilizzare DynamicObject posto di ExpandoObject e in questo modo si ha solo il sovraccarico di fare il reflection se si tenta effettivamente di accedere a una proprietà dall’altro object.

 public class DynamicForwarder : DynamicObject { private object _target; public DynamicForwarder(object target) { _target = target; } public override bool TryGetMember( GetMemberBinder binder, out object result) { var prop = _target.GetType().GetProperty(binder.Name); if (prop == null) { result = null; return false; } result = prop.GetValue(_target, null); return true; } } 

Ora fa solo il riflesso quando si tenta effettivamente di accedere alla proprietà tramite un get dinamico. Sul lato negativo, se si accede ripetutamente alla stessa proprietà, deve fare il riflesso ogni volta. Quindi potresti memorizzare nella cache il risultato:

 public class DynamicForwarder : DynamicObject { private object _target; private Dictionary _cache = new Dictionary(); public DynamicForwarder(object target) { _target = target; } public override bool TryGetMember( GetMemberBinder binder, out object result) { // check the cache first if (_cache.TryGetValue(binder.Name, out result)) return true; var prop = _target.GetType().GetProperty(binder.Name); if (prop == null) { result = null; return false; } result = prop.GetValue(_target, null); _cache.Add(binder.Name, result); // < -------- insert into cache return true; } } 

È ansible supportare l'archiviazione di un elenco di oggetti di destinazione per unire le proprietà e supportare le proprietà di impostazione (con una sostituzione simile chiamata TrySetMember ) per consentire di impostare dynamicmente i valori nel dizionario della cache.

Ovviamente, il sovraccarico della riflessione probabilmente non vale la pena di preoccuparsi, ma per gli oggetti di grandi dimensioni questo potrebbe limitare l'impatto di esso. Ciò che è forse più interessante è la flessibilità extra che ti dà.

Questa è una vecchia domanda, ma ora dovresti riuscire a farlo con il seguente codice:

 dynamic expObj = new ExpandoObject(); expObj.Name = "James Kirk"; expObj.Number = 34; // print the dynamically added properties // enumerating over it exposes the Properties and Values as a KeyValuePair foreach (KeyValuePair kvp in expObj){ Console.WriteLine("{0} = {1} : Type: {2}", kvp.Key, kvp.Value, kvp.Value.GetType()); } 

L’output sarà simile al seguente:

Nome = James Kirk: Digitare: System.String

Numero = 34: Tipo: System.Int32

Utilizzare Reflection.Emit per creare un metodo generico per riempire un object ExpandoObject.

O usare le espressioni forse (penso che sarebbe ansible solo in .NET 4).

Nessuno di questi approcci utilizza la riflessione durante il richiamo, solo durante l’installazione di un delegato (che ovviamente deve essere memorizzato nella cache).

Ecco un codice Reflection.Emit per riempire un dizionario (immagino che ExpandoObject non sia lontano);

 static T CreateDelegate(this DynamicMethod dm) where T : class { return dm.CreateDelegate(typeof(T)) as T; } static Dictionary>> cache = new Dictionary>>(); static Dictionary GetProperties(object o) { var t = o.GetType(); Func> getter; if (!cache.TryGetValue(t, out getter)) { var rettype = typeof(Dictionary); var dm = new DynamicMethod(t.Name + ":GetProperties", rettype, new Type[] { typeof(object) }, t); var ilgen = dm.GetILGenerator(); var instance = ilgen.DeclareLocal(t); var dict = ilgen.DeclareLocal(rettype); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Castclass, t); ilgen.Emit(OpCodes.Stloc, instance); ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes)); ilgen.Emit(OpCodes.Stloc, dict); var add = rettype.GetMethod("Add"); foreach (var prop in t.GetProperties( BindingFlags.Instance | BindingFlags.Public)) { ilgen.Emit(OpCodes.Ldloc, dict); ilgen.Emit(OpCodes.Ldstr, prop.Name); ilgen.Emit(OpCodes.Ldloc, instance); ilgen.Emit(OpCodes.Ldfld, prop); ilgen.Emit(OpCodes.Castclass, typeof(object)); ilgen.Emit(OpCodes.Callvirt, add); } ilgen.Emit(OpCodes.Ldloc, dict); ilgen.Emit(OpCodes.Ret); cache[t] = getter = dm.CreateDelegate>>(); } return getter(o); } 

devi usare la riflessione …. ( codice “preso in prestito” da questo URL )

 using System.Reflection; // reflection namespace // get all public static properties of MyClass type PropertyInfo[] propertyInfos; propertyInfos = typeof(MyClass).GetProperties(BindingFlags.Public | BindingFlags.Static); // sort properties by name Array.Sort(propertyInfos, delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2) { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); }); // write property names foreach (PropertyInfo propertyInfo in propertyInfos) { Console.WriteLine(propertyInfo.Name); }