我想将一个匿名对象作为方法的参数,然后迭代其属性以将每个属性/值添加到动态ExpandoObject
。
所以我需要的是从
开始new { Prop1 = "first value", Prop2 = SomeObjectInstance, Prop3 = 1234 }
知道每个属性的名称和值,并能够将它们添加到ExpandoObject
。
我如何做到这一点?
旁注:这将在我的许多单元测试中完成(我用它来重构设置中的大量垃圾),因此性能在某种程度上是相关的。我不太了解反思,肯定地说,但从我所知道的那里,它的性能非常重,所以如果有可能我宁愿避免它...
后续问题:
正如我所说,我将这个匿名对象作为方法的参数。我应该在方法的签名中使用什么数据类型?如果我使用object
?
答案 0 :(得分:67)
foreach(var prop in myVar.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
Console.WriteLine("Name: {0}, Value: {1}",prop.Name, prop.GetValue(myVar,null));
}
答案 1 :(得分:6)
反映匿名对象以获取其属性名称和值,然后利用ExpandoObject实际上是一个字典来填充它。这是一个例子,表示为单元测试:
[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");
}
答案 2 :(得分:3)
另一种方法是使用DynamicObject
而不是ExpandoObject
,这样,如果您实际尝试从另一个对象访问属性,那么您只需要进行反射的开销。
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;
}
}
现在它只在你真正尝试通过动态get访问属性时进行反射。不利的一面是,如果您反复访问同一个属性,则每次都必须进行反射。所以你可以缓存结果:
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的类似覆盖),以允许您在缓存字典中动态设置值。
当然,反射的开销可能不值得担心,但对于大型物体,这可能会限制它的影响。更有趣的是它给你带来的额外灵活性。
答案 3 :(得分:2)
这是一个老问题,但现在您应该可以使用以下代码执行此操作:
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());
}
输出如下所示:
Name = James Kirk:Type:System.String
数字= 34:输入: System.Int32
答案 4 :(得分:0)
你必须使用反射....(code "borrowed" from this 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);
}
答案 5 :(得分:0)
使用Reflection.Emit创建一个通用方法来填充ExpandoObject。
或者也许使用表达式(我认为这只能在.NET 4中实现)。
这两种方法都不会在调用时使用反射,只是在设置委托时(显然需要缓存)。
这里有一些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);
}