【问题标题】:Use reflection "GetValue" to return information from Dictionary使用反射“GetValue”从 Dictionary 返回信息
【发布时间】:2016-07-18 03:12:38
【问题描述】:

--EDIT-- 重写以更好地阐明我想要做什么,以及问题是什么。抱歉,它不简洁,但上下文可能有助于理解。

我有一个程序,可以针对各种场景运行多个模拟。我将这些模拟和场景的结果存储在一个类(“结果类”)中,将基本结果存储为类的属性,并将各种场景和敏感性存储在类内的字典中。

因为不同的用户会希望从模拟中获得不同的输出,所以我正在尝试开发一种允许用户生成自定义报告的方法。为此,我使用反射。我使用反射来处理结果类中的每个属性,并将其添加到树视图中,使用与报告类相同的层次结构。然后,用户可以浏览树形视图,查看每个属性,并在报告中选择他们想要的属性。

当用户选择要报告的属性时,我使用树层次结构将其转换为“路径” - 一个分隔字符串,用于描述感兴趣项目的属性分支:例如:

Results.TotalCapacity; or:
Results.UsableCapacity; or
Results.Scenarios[HighCase].TotalCapacity; or
Results.Sensitivity.ModifiedCapacity.

最终,我想使用反射来解析这些路径,并从中检索值。

我从 this link 大量借用了代码,但是当路径中的对象之一是字典时,我正在努力寻找检索适当对象的最佳方法。

我已经设法让它发挥作用,但如果能提供任何关于如何改进或使其更强大的反馈,我将不胜感激。将字典转换为列表然后通过索引获取键显然不是最佳选择。我可以修改我的“路径”代码以返回字典键,但不确定如何使用反射从键中获取字典值。

代码如下:

public object GetPropertyValueFromPath(object baseObject, string path)
{
    //Split into the base path elements
    string[] pp = CorrectPathForDictionaries(path).Split(new[]{ '.' }, StringSplitOptions.RemoveEmptyEntries);

    //Set initial value of the ValueObject
    object valueObject = baseObject;            
    foreach (var prop in pp)
    {
        if (prop.Contains("["))
        {
            //Will be a dictionary.  Get the name of the dictionary, and the index element of interest:
            string dictionary = prop.Substring(0, prop.IndexOf("["));
            int index = Convert.ToInt32(prop.Substring(prop.IndexOf("[") + 1, prop.Length - prop.IndexOf("]")));

            //Get the property info for the dictionary
            PropertyInfo dictInfo = valueObject.GetType().GetProperty(dictionary);
            if (dictInfo != null)
            {
                //Get the dictionary from the PropertyInformation
                valueObject = dictInfo.GetValue(valueObject, null);
                //Convert it to a list to provide easy access to the item of interest.  The List<> will be a set of key-value pairs.
                List<object> values = ((IEnumerable)valueObject).Cast<object>().ToList();
                //Use "GetValue" with the "value" parameter and the index to get the list object we want.
                valueObject = values[index].GetType().GetProperty("Value").GetValue(values[index], null); 
            }
        }
        else
        {
            PropertyInfo propInfo = valueObject.GetType().GetProperty(prop);
            if (propInfo != null)
            {
                valueObject = propInfo.GetValue(valueObject, null);
            }
        }
    }
    return valueObject;
}

【问题讨论】:

  • 顺便说一句:“CorrectPathForDictionaries”只是获取路径并更正它,以便将 Scenario.[3].ModifiedCapacity 更改为 'Scenario[3].ModifiedCapacity.
  • 你试过什么?请附上一个很好的minimal reproducible example,清楚地展示您尝试过的内容,并准确描述您遇到的具体问题。请注意,在编译时您不知道类型参数的字典上反射的相关成员(如果您知道,那么当然您可以将对象转换为正确的字典类型)是Item 属性。

标签: c# dictionary reflection


【解决方案1】:

好的...不是世界上最漂亮的代码,但它可以工作(感谢任何改进建议)。

我测试看看我是否试图从字典中获取一个值。如果是这样,我会得到字典的名称,以及请求的元素的索引。我在 Dictionary Name 上使用 GetProperty 来获取 PropertyInfo 对象,然后在该 PropertyInfo 上使用 GetValue 来获取实际的字典。作为处理键值对的一种方法,我将字典转换为一个列表,然后使用索引来获取单个键值对。然后我调用 GetProperty("Value") 从字典中获取所需的对象。

public object GetPropertyValueFromPath(object baseObject, string path)
    {
        //Split into the base path elements
        string[] pp = CorrectPathForDictionaries(path).Split(new[]{ '.' }, StringSplitOptions.RemoveEmptyEntries);

        //Set initial value of the ValueObject
        object valueObject = baseObject;            
        foreach (var prop in pp)
        {
            if (prop.Contains("["))
            {
                //Will be a dictionary.  Get the name of the dictionary, and the index element of interest:
                string dictionary = prop.Substring(0, prop.IndexOf("["));
                int index = Convert.ToInt32(prop.Substring(prop.IndexOf("[") + 1, prop.Length - prop.IndexOf("]")));

                //Get the property info for the dictionary
                PropertyInfo dictInfo = valueObject.GetType().GetProperty(dictionary);
                if (dictInfo != null)
                {
                    //Get the dictionary from the PropertyInformation
                    valueObject = dictInfo.GetValue(valueObject, null);
                    //Convert it to a list to provide easy access to the item of interest.  The List<> will be a set of key-value pairs.
                    List<object> values = ((IEnumerable)valueObject).Cast<object>().ToList();
                    //Use "GetValue" with the "value" parameter and the index to get the list object we want.
                    valueObject = values[index].GetType().GetProperty("Value").GetValue(values[index], null); 
                }
            }
            else
            {
                PropertyInfo propInfo = valueObject.GetType().GetProperty(prop);
                if (propInfo != null)
                {
                    valueObject = propInfo.GetValue(valueObject, null);
                }
            }
        }
        return valueObject;
    }

【讨论】:

  • 字典是无序的,所以按索引检索元素肯定会做错事。如果它确实有效,那纯属巧合。
  • 它们被加载到树视图中,并使用相同的代码检索,因此顺序相同,但我明白你的意思。
【解决方案2】:

缺少一个好的Minimal, Complete, and Verifiable example 可以清楚地显示您尝试过的内容,以及清楚地解释您遇到的具体困难,我不太清楚您在这里问什么。但是,似乎您可能在从字典对象中检索给定键的值时遇到问题,而您在编译时不知道其类型(因为如果您确实知道编译时的类型时间,大概您只是转换为该类型,然后直接使用字典)。

如果这是正确的,那么这应该会有所帮助:

有两种方法可以做到这一点。一种是通过反射直接访问对象,使用Item 属性,这是类的索引器属性的实际编译名称。看起来像这样:

private static string GetValue(object dictionary, object key)
{
    PropertyInfo itemProperty = dictionary.GetType().GetProperty("Item");

    return itemProperty.GetValue(dictionary, new object[] { key }).ToString();
}

直接反射:获取PropertyInfo对象,然后将key值传递给GetValue()方法获取值。

这很好用,但有点笨拙,每次想要获取值时都需要通过反射来检查类型。您可以通过缓存 PropertyInfo 对象来改进后者,但它仍然很笨拙。

您可以改为让运行时完成缓存工作,同时允许代码读取更接近普通字典查找的样子,方法是使用 dynamic 类型:

private static string GetValue(dynamic dictionary, dynamic key)
{
    return dictionary[key].ToString();
}

请注意,dictionary 对象和key 都必须是dynamic。当您以这种方式编写方法时,在运行时将根据dynamic 变量的类型和使用它们的代码表达式创建一个“绑定器”。本质上,代码表达式的最终编译被推迟到类型已知的运行时,以便它可以正常执行。 (如果您要使用不同类型执行相同的语句,运行时将为每种类型组合创建不同的绑定器。)

这两种方法都解决了在编译时类型未知时获取给定键值的具体问题。如果这不能解决您的问题,请根据要求提供良好的 MCVE,并准确描述该代码的作用以及您希望它做什么来改进问题。

【讨论】:

    猜你喜欢
    • 2021-09-26
    • 1970-01-01
    • 2021-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多