我如何反映动态对象的成员?

Fla*_*DOA 123 c# reflection dynamic

我需要从.NET 4中使用dynamic关键字声明的对象中获取属性及其值的字典?似乎使用反射这是行不通的.

例:

dynamic s = new ExpandoObject();
s.Path = "/Home";
s.Name = "Home";

// How do I enumerate the Path and Name properties and get their values?
IDictionary<string, object> propertyValues = ???
Run Code Online (Sandbox Code Playgroud)

ito*_*son 100

在ExpandoObject的情况下,ExpandoObject类实际上IDictionary<string, object>为其属性实现,因此解决方案与转换一样简单:

IDictionary<string, object> propertyValues = (IDictionary<string, object>)s;
Run Code Online (Sandbox Code Playgroud)

请注意,这不适用于一般动态对象.在这些情况下,您需要通过IDynamicMetaObjectProvider下拉到DLR.

  • 这仅适用于 ExpandoObject 类的对象,DynamicObject 类是另一个不实现 IDictionary 而是实现 IDynamicMetaObjectProvider 的可扩展类。 (2认同)

Ale*_*ina 57

有几种情况需要考虑.首先,您需要检查对象的类型.您可以直接调用GetType().如果类型未实现IDynamicMetaObjectProvider,则可以使用与任何其他对象相同的反射.就像是:

var propertyInfo = test.GetType().GetProperties();
Run Code Online (Sandbox Code Playgroud)

但是,对于IDynamicMetaObjectProvider实现,简单反射不起作用.基本上,您需要了解有关此对象的更多信息.如果它是ExpandoObject(它是IDynamicMetaObjectProvider实现之一),您可以使用itowlson提供的答案.ExpandoObject将其属性存储在字典中,您只需将动态对象转换为字典即可.

如果它是DynamicObject(另一个IDynamicMetaObjectProvider实现),那么您需要使用此DynamicObject公开的任何方法.DynamicObject不需要在任何地方实际"存储"其属性列表.例如,它可能会做这样的事情(我正在重复使用我博客文章中的一个例子):

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,每当您尝试访问属性(具有任何给定名称)时,该对象只是将该属性的名称作为字符串返回.

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".
Run Code Online (Sandbox Code Playgroud)

所以,你没有任何东西可以反映 - 这个对象没有任何属性,同时所有有效的属性名称都可以工作.

我要说的是,对于IDynamicMetaObjectProvider实现,您需要过滤已知的实现,您可以在其中获取属性列表,例如ExpandoObject,并忽略(或抛出异常).

  • 在我看来,如果我消耗的类型是动态的,那么我不应该对它的底层类型做出假设.我应该可以调用GetDynamicMemberNames来获取成员列表.静态类型比动态类型具有更好的运行时检查支持似乎是愚蠢的! (7认同)
  • 您可以覆盖动态对象的GetDynamicMemberNames()方法,以返回动态成员的列表名称.问题是不能保证每个动态对象都有这个方法(ExpandoObject没有).反射对于静态类型更有效,这并不奇怪.它首先是为他们创造的.通过动态,您必须更多地依赖于单元测试和TDD. (2认同)

jbt*_*ule 30

如果IDynamicMetaObjectProvider可以提供动态成员名称,则可以获取它们.请参阅apache许可的PCL库Dynamitey中的GetMemberNames实现(可以在nuget中找到),它适用于实现的s和s 以及提供没有自定义测试的实现的元对象的任何其他实现.ExpandoObjectDynamicObjectGetDynamicMemberNamesIDynamicMetaObjectProviderGetDynamicMemberNamesis IDynamicMetaObjectProvider

在获得成员名称之后,以正确的方式获得价值需要更多的工作,但Impromptu会做到这一点,但是更难指向有趣的位并让它有意义.这是文档,它与反射相同或更快,但是,它不可能比expando的字典查找更快,但它适用于任何对象,expando,动态或原始 - 你可以命名它.

  • 感谢到达以下代码:var tTarget = target as IDynamicMetaObjectProvider; if(tTarget!= null){tList.AddRange(tTarget.GetMetaObject(Expression.Constant(tTarget)).GetDynamicMemberNames()); } (16认同)

Mr.*_*. B 17

需要Newtonsoft Json.Net

有点晚了,但我想出了这个.它只为您提供了键,然后您可以在动态上使用它们:

public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
    JObject attributesAsJObject = dynamicToGetPropertiesFor;
    Dictionary<string, object> values = attributesAsJObject.ToObject<Dictionary<string, object>>();
    List<string> toReturn = new List<string>();
    foreach (string key in values.Keys)
    {
        toReturn.Add(key);                
    }
    return toReturn;
}
Run Code Online (Sandbox Code Playgroud)

然后你只需这样预告:

foreach(string propertyName in GetPropertyKeysForDynamic(dynamicToGetPropertiesFor))
{
    dynamic/object/string propertyValue = dynamicToGetPropertiesFor[propertyName];
    // And
    dynamicToGetPropertiesFor[propertyName] = "Your Value"; // Or an object value
}
Run Code Online (Sandbox Code Playgroud)

选择将值作为字符串或其他对象获取,或者执行另一个动态并再次使用查找.

  • 完善!我不得不改变JObject属性AsJObject = dynamicToGetPropertiesFor; to Object attributesAsJObject =(JObject)JToken.FromObject(obj); 虽然!! (3认同)