如何迭代C#中的匿名对象的属性?

我想将一个匿名对象作为parameter passing给一个方法,然后遍历它的属性来将每个属性/值添加到一个dynamic的ExpandoObject

所以我需要的是离开

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

知道每个属性的名称和值,并能够将它们添加到ExpandoObject

我如何做到这一点?

附注:这将在我的许多unit testing中完成(我正在使用它来重构设置中的很多垃圾),所以性能在一定程度上是相关的。 我对reflection的确切知之甚less,但是从我所了解的情况来看,它的performance很重,所以如果可能的话,我宁愿避免它。

后续问题:正如我所说的,我将这个匿名对象作为方法的参数。 我应该在方法的签名中使用哪种数据types? 如果我使用object所有的属性都可用吗?

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

反思匿名对象以获取其属性名称和值,然后利用实际为字典的ExpandoObject来填充它。 下面是一个例子,表示为一个unit testing:

  [TestMethod] public void ShouldBeAbleToConvertAnAnonymousObjectToAnExpandoObject() { var additionalViewData = new {id = "myControlId", css = "hide well"}; dynamic result = new ExpandoObject(); var dict = (IDictionary<string, object>)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"); } 

另一种方法是使用DynamicObject而不是ExpandoObject ,这样,如果您真的试图从另一个对象访问某个属性,那么只有执行reflection的开销。

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

现在只有当你真正尝试通过dynamic获取访问属性时才会进行reflection。 不利的一面是,如果你反复访问同一个属性,每次都必须进行reflection。 所以你可以caching结果:

 public class DynamicForwarder : DynamicObject { private object _target; private Dictionary<string, object> _cache = new Dictionary<string, object>(); 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; } } 

您可以支持存储目标对象列表以合并其属性,并支持设置属性(使用类似的替代方法,称为TrySetMember ),以便dynamic设置caching字典中的值。

当然,reflection的开销可能不值得担心,但是对于大型对象来说,这可能会限制它的影响。 也许更有趣的是它给你的额外的灵活性。

使用Reflection.Emit创build一个通用方法来填充ExpandoObject。

或者使用expression式也许(我认为这只会在.NET 4中可能)。

这些方法都不是在调用的时候使用reflection,只是在代理的设置过程中(显然需要被caching)。

这里有一些Reflection.Emit代码来填充一个字典(我猜ExpandoObject不是很远);

 static T CreateDelegate<T>(this DynamicMethod dm) where T : class { return dm.CreateDelegate(typeof(T)) as T; } static Dictionary<Type, Func<object, Dictionary<string, object>>> cache = new Dictionary<Type, Func<object, Dictionary<string, object>>>(); static Dictionary<string, object> GetProperties(object o) { var t = o.GetType(); Func<object, Dictionary<string, object>> getter; if (!cache.TryGetValue(t, out getter)) { var rettype = typeof(Dictionary<string, object>); 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<Func<object, Dictionary<string, object>>>(); } return getter(o); } 

这是一个古老的问题,但现在你应该可以用下面的代码来做到这一点:

 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<string, object> kvp in expObj){ Console.WriteLine("{0} = {1} : Type: {2}", kvp.Key, kvp.Value, kvp.Value.GetType()); } 

输出将如下所示:

名称= James Kirk:types:System.String

数字= 34:types:System.Int32

你必须使用reflection….( 代码“借”从这个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); }