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


131

我需要从使用.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 = ???

Answers:


32

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

在获得成员名称之后,要以正确的方式获得价值还需要做更多的工作,但是Impromptu可以做到这一点,但要指出有趣的部分并使其有意义就更加困难了。这是文档,它等于或比反射更快,但是,它不可能比对expando的字典查找要快,但它可用于任何对象,expando,动态或原始对象-随便命名。


17
到目前为止,代码是:var tTarget = target as IDynamicMetaObjectProvider; 如果(tTarget!= null){tList.AddRange(tTarget.GetMetaObject(Expression.Constant(tTarget))。GetDynamicMemberNames()); }
Flatliner DOA

谢谢你的图书馆。我在将动态对象往返于该库dynamicjson.codeplex.com时遇到了问题,并且能够使用您的库进行扩展。
JJS

1
请举个例子。
Demodave

105

对于ExpandoObject而言,ExpandoObject类实际上IDictionary<string, object>是为其属性实现的,因此该解决方案与强制转换一样简单:

IDictionary<string, object> propertyValues = (IDictionary<string, object>)s;

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


为此,不幸的是样本有点过分简化了。我需要能够检查动态对象而不知道它的真实类型是什么。
Flatliner DOA 2010年

2
这仅适用于ExpandoObject类的对象,DynamicObject类是另一个可扩展类,它不实现IDictionary,而是实现IDynamicMetaObjectProvider。
Sharique Abdullah

1
它确实回答了OP的问题。
Sharique Abdullah

58

有几种情况需要考虑。首先,您需要检查对象的类型。您可以为此简单地调用GetType()。如果该类型未实现IDynamicMetaObjectProvider,则可以与其他任何对象一样使用反射。就像是:

var propertyInfo = test.GetType().GetProperties();

但是,对于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;
    }
}

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

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

因此,您没有任何可以反映的内容-该对象没有任何属性,同时所有有效的属性名称都可以使用。

我想说的是,对于IDynamicMetaObjectProvider实现,您需要对已知的实现进行过滤,在其中可以获取诸如ExpandoObject之类的属性列表,而其余部分则忽略(或抛出异常)。


7
在我看来,如果我使用的类型是动态类型,则我不应该对其基础类型进行假设。我应该能够调用GetDynamicMemberNames来获取成员列表。静态类型比动态类型具有更好的运行时检查支持似乎很愚蠢!
Flatliner DOA

2
您可以为动态对象覆盖GetDynamicMemberNames()方法,以返回动态成员的列表名称。问题在于,不能保证每个动态对象都具有此方法(ExpandoObject没有)。反射对于静态类型更好地工作并不奇怪。它是最初为他们创建的。使用动力学,您必须更多地依赖于单元测试和TDD。
亚历山德拉·鲁西纳

1
MSDN文档GetDynamicMemberNames()提到:“此方法仅出于调试目的而存在。”,并不是真正令人安慰的:(
NicoJuicy

19

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

然后,您只需像这样进行foreach:

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

选择以字符串或其他对象的形式获取值,或者进行另一个动态处理并再次使用查找。


您不需要列表返回。
aloisdg移至codidact.com,2016年

4
完善!我不得不更改JObject propertiesAsJObject = dynamicToGetPropertiesFor; 到对象attributeAsJObject =(JObject)JToken.FromObject(obj); 虽然!!
k25 17-10-25

只是重申@ k25所说的话。您需要替换JObject attributesAsJObject = dynamicToGetPropertiesFor;为:var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);。届时,您可以执行类似的操作来获取属性名称和值的字典var objProperties = jObject.ToObject<Dictionary<string, object>>();。有了这个,您就可以参加比赛了。这不需要动态。它适用于DynamicObject
Flydog57 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.