【问题标题】:How do I reflect over the members of dynamic object?如何反映动态对象的成员?
【发布时间】:2011-02-07 18:09:44
【问题描述】:

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

【问题讨论】:

    标签: c# dynamic reflection


    【解决方案1】:

    在 ExpandoObject 的情况下,ExpandoObject 类实际上为它的属性实现了IDictionary&lt;string, object&gt;,所以解决方法就像强制转换一样简单:

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

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

    【讨论】:

    • 感谢您,不幸的是,示例有点过于简单化了。我需要能够在不知道它的真实类型是什么的情况下检查动态对象。
    • 这仅适用于 ExpandoObject 类的对象,DynamicObject 类是另一个不实现 IDictionary 而是实现 IDynamicMetaObjectProvider 的可扩展类。
    • 它确实回答了 OP 的问题。
    【解决方案2】:

    有几种情况需要考虑。 首先,您需要检查对象的类型。为此,您可以简单地调用 GetType()。 如果该类型没有实现 IDynamicMetaObjectProvider,那么您可以使用与任何其他对象相同的反射。比如:

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

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

    如果它是 DynamicObject(另一个 IDynamicMetaObjectProvider 实现),那么您需要使用此 DynamicObject 公开的任何方法。 DynamicObject 不需要在任何地方实际“存储”其属性列表。例如,它可能会做这样的事情(我正在重用来自my blog post 的示例):

    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,其余部分忽略(或抛出异常)。

    【讨论】:

    • 在我看来,如果我使用的类型是动态的,那么我不应该假设它是底层类型。我应该能够调用 GetDynamicMemberNames 来获取成员列表。静态类型比动态类型具有更好的运行时检查支持似乎很愚蠢!
    • 您可以覆盖动态对象的 GetDynamicMemberNames() 方法以返回动态成员的列表名称。问题是不能保证每个动态对象都有这个方法(ExpandoObject 没有)。反射更适合静态类型也就不足为奇了。它首先是为他们创建的。对于动态,您必须更多地依赖单元测试和 TDD。
    • GetDynamicMemberNames() 的 MSDN 文档提到:“此方法仅用于调试目的。”,并不是很令人欣慰:(
    【解决方案3】:

    如果 IDynamicMetaObjectProvider 可以提供动态成员名称,您可以获得它们。请参阅 apache 许可 PCL 库 Dynamitey 中的 GetMemberNames 实现(可以在 nuget 中找到),它适用于实现 GetDynamicMemberNamesExpandoObjects 和 DynamicObjects 以及提供元数据的任何其他 IDynamicMetaObjectProvider具有GetDynamicMemberNames 实现的对象,没有超出is IDynamicMetaObjectProvider 的自定义测试。

    在获取成员名称之后,要以正确的方式获取值需要做更多的工作,但 Impromptu 会这样做,但很难仅指出有趣的部分并使其有意义。这是documentation,它与反射相同或更快,但是,它不太可能比expando 的字典查找更快,但它适用于任何对象,expando,动态或原始 - 你可以命名它。

    【讨论】:

    • 感谢直截了当,代码是: var tTarget = target as IDynamicMetaObjectProvider; if (tTarget !=null) { tList.AddRange(tTarget.GetMetaObject(Expression.Constant(tTarget)).GetDynamicMemberNames()); }
    • 感谢您的出色图书馆。我在将动态对象往返到此库 dynamicjson.codeplex.com 时遇到问题,并且能够使用您的库对其进行扩展。
    • 请举例。
    【解决方案4】:

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

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

    【讨论】:

    • 您不需要返回列表。
    • 完美!我不得不改变 JObject attributesAsJObject = dynamicToGetPropertiesFor;对象属性AsJObject = (JObject)JToken.FromObject(obj);不过!!
    • 只是重申@k25 所说的话。您需要将JObject attributesAsJObject = dynamicToGetPropertiesFor; 替换为:var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);。此时,您可以通过执行var objProperties = jObject.ToObject&lt;Dictionary&lt;string, object&gt;&gt;(); 之类的操作来获取属性名称和值的字典。有了它,您就可以参加比赛了。这不需要动态。它适用于 DynamicObject 的任何子类
    【解决方案5】:

    仅反映 json 对象的简单解决方案:

    using System.Collections.Generic;
    using System.Text.Json;
    dynamic d = new {a = 1, b = 2, c = 3};
    foreach ((string k, object o) in JsonSerializer.Deserialize<Dictionary<string, object>>(JsonSerializer.Serialize(d)))
    {
    
    }
    

    【讨论】:

      猜你喜欢
      • 2019-12-27
      • 1970-01-01
      • 2011-01-05
      • 2018-10-14
      • 2011-12-01
      • 2020-10-25
      • 2020-02-17
      • 1970-01-01
      相关资源
      最近更新 更多