【问题标题】:In C#, how to access a dictionary with items of anonymous type wrapped inside of an "object" type在 C# 中,如何访问包含在“对象”类型中的匿名类型项的字典
【发布时间】:2020-01-27 20:21:25
【问题描述】:

在 C# 中,我正在读取IDictionary<string, object> 类型的属性Config。我无法访问objectelements 中的信息。它应该是一个 JSON 字符串,但是它被包装为对象。

在 VisualStudio 的即时窗口中,输入 Config 输出:

Count = 1
    [0]: {[items, Count = 3]}

深入研究项目:

var configItemsElement  = Config["items"]

在即时窗口输出中输入configItemsElement

Count = 3
    [0]: {[1, {{ value = SeoChoiceInherit, sortOrder = 1 }}]}
    [1]: {[2, {{ value = SeoChoiceYes, sortOrder = 2 }}]}
    [2]: {[3, {{ value = SeoChoiceNo, sortOrder = 3 }}]}

输入configItemsElement.ToString() 会返回此类型信息:

System.Collections.Generic.Dictionary`2[System.String,<>f__AnonymousType7`2[System.String,System.Int32]]

据我所知。我无法访问configItemsElement 的元素。比如

configItemsElement[1]

结果为@​​987654333@

在 VisualStudio 的“Autos”窗口中,configItemsElement的信息是

configItemsElement
Value: Count = 3
Type: object {System.Collections.Generic.Dictionary<string, <>f__AnonymousType7<string, int>>}

type 信息是什么意思?它提示字典,但应用带有configItemsElement[1] 的键会导致上述错误。如何将此对象转换为我可以使用的类型?我尝试的任何演员都会导致错误。

编辑: 我无权访问提供 .Config 属性的代码,但我可以在 SQL 资源管理器中看到正在读入属性的数据。 SQL Explorer 显示了这些数据:

{"items":[{"id":1,"value":"SeoChoiceInherit"},{"id":2,"value":"SeoChoiceYes"},{"id":3,"value":"SeoChoiceNo"}]}

按照 symap 的回答中的建议,使 configItemsElement 动态化并访问 .value,例如:

dynamic configItemsElement = Config["Items"];
var myString =configItemsElement.value;

导致错误:

''object' does not contain a definition for 'value''

据我了解,因为 configItemsElement 应该包含字典。但是,如上所述,访问字典元素也不起作用。

【问题讨论】:

  • 这可能有助于显示有错误的演员尝试
  • 请分享它是如何在代码中声明的
  • 似乎configItemsElement 是一个字典,其中string 是键,值是int 类型。你能告诉我们结构吗configItemsElement
  • 我无法访问提供IDictionary&lt;string, object&gt; 类型的.Config 属性的代码。但是我可以在 SQL 资源管理器中看到似乎读入此属性的数据: {"items":[{"id":1,"value":"SeoChoiceInherit"},{"id":2,"value" :"SeoChoiceYes"},{"id":3,value":"#SeoChoiceNo"}]}
  • @Attersson:例如configItemsElement as string 给出null。我还定义了一些类来镜像包含的类型并明确地转换为此,我得到'指定的转换无效`

标签: c#


【解决方案1】:

System.Collections.Generic.Dictionary&lt;string, &lt;&gt;f__AnonymousType7&lt;string, int&gt;&gt; 类型表示您有一个字典,其中键是字符串,值是 anonymous types。由于匿名类型没有名称(无论如何从 C# 的角度来看,您可以看到它们在编译的代码中获得了生成的名称),因此没有可用的类型供您将值转换为。

您的问题发生在从 JSON 解析配置的位置。匿名类型通常应该只在单个方法内部使用。如果它们作为object 引用泄露出去,您就会遇到问题。如果可能,应将生成这些配置元素的代码固定为使用命名类型。

但是,如果这不可能,您唯一的后备方法是强制转换值 dynamic

【讨论】:

  • 我遇到的麻烦是如何访问字典的元素。即使我使configItemsElement 动态化,仍然configItemsElement[1] 例如抛出。
【解决方案2】:

您可以使用动态访问成员而无需类型检查。

using System;
using System.Collections;
using System.Collections.Generic;

namespace ObjectDictionary
{
    class Program
    {
        static void Main(string[] args)
        {

            IDictionary <string, object>  dict= new Dictionary<String, object>();
            dict.Add("abc", new {value = "blah", sortOrder = 1});

            dynamic a = dict["abc"];
            Console.WriteLine(a.value);
            Console.WriteLine(a.sortOrder);
        }
    }
}

所以你的

var configItemsElement  = Config["items"];

会变成

dynamic configItemsElement = Config["Items"];
var myString =configItemsElement.value;

查看您的问题,数据似乎没有存储为 Json。如果您需要它作为 json,那么您需要重建 json。 您可能可以使用 json 序列化器

 string json = JsonConvert.SerializeObject(configItemsElement, Formatting.Indented);
            Console.WriteLine(json);

【讨论】:

  • 'var myString =configItemsElement.value' 给我一个错误''object' does not contain a definition for 'value''。请查看我的编辑以及一些附加信息。
  • P.S.:不,我不需要 JSON 格式的数据。我做了这个评论,源数据可能是 JSON,以防它有助于理解问题。
  • 序列化它会给我一个"{\"1\":{\"value\":\"SeoChoiceInherit\",\"sortOrder\":1},\"2\":{\"value\":\"SeoChoiceYes\",\"sortOrder\":2},\"3\":{\"value\":\"SeoChoiceNo\",\"sortOrder\":3}}"的字符串。我想知道如何从dynamic configItemsElementitself 访问各个元素(1、2、3)。
  • 在您的示例代码中,dynamic a = dict["abc"]; 正是在我的情况下不起作用的。 dictmaps 到我的 configItemElement, but configItemElement["1"]` 抛出 error CS0021: Cannot apply indexing with [] to an expression of type 'object'
【解决方案3】:

我回答了我自己的问题,因为我收到的宝贵意见为我指明了正确的方向,但没有给我一个可行的解决方案。我对 Stackoverflow 比较陌生,所以如果有什么我应该了解的,请告诉我。

另外,虽然我找到了一个可行的解决方案,但我不明白为什么 @symaps 和 @shf301 的建议在这种情况下不起作用。这就是为什么我感谢您提供进一步的意见。

我最终让它工作的方法是:

  • 为匿名类型声明我自己的类型
  • configItemsElement 转换为 IEnumerable
  • 遍历configItemsElement内部的字典
  • 使用反射获取每个字典项的“内部项”
  • 将“内部项目”(匿名类型)转换为动态
  • 并使用反射访问“内部项目”的 2 个属性(valuesortOrder)以将它们添加到新列表中

这是我的工作代码:

...

public class InnerItemType
{
    public int sortOrder { get; set;  }
    public string value { get; set; }
}

....

List<InnerItemType> listOptions = new List<InnerItemType>();

var configItemsElement = Config["items"] as IEnumerable;

foreach (var item in configItemsElement)
{
    dynamic innerItem = item.GetType().GetProperties()[1].GetValue(item);
    listOptions.Add(new InnerItemType() { 
        value = innerItem.GetType().GetProperties()[0].GetValue(innerItem), 
        sortOrder = innerItem.GetType().GetProperties()[1].GetValue(innerItem)
    });
}
...

感谢@symaps 指出需要使用动态。感谢@shf301 解释匿名类型是造成困难的原因。这为我指明了正确的方向。

但是,奇怪的是,在我的情况下,仅动态(没有反射)的解决方案无法按建议工作,我不明白为什么会这样。

item.GetType().GetProperties()[1] 透露了 innerItem 的名称:

{<>f__AnonymousType7`2[System.String,System.Int32] Value}
    ...
    Name: "Value"

但如果我尝试使用这样的属性名称通过动态类型访问 innerItem:

foreach (dynamic item in configItemsElement)
{
    dynamic innerItem = item.Value;

我收到一个错误"'System.ValueType' does not contain a definition for 'Value'"

同样,如果我尝试直接访问 innerItem.valueinnerItem.sortOrder 而无需像这样进行反射:

foreach (var item in configItemsElement)
{
    dynamic innerItem = item.GetType().GetProperties()[1].GetValue(item);
    listOptions.Add(new InnerItemType() { 
        value = innerItem.value, 
        sortOrder = innerItem.sortOrder
    });

给出同样的错误。

我是不是对dynamic的作品有误解?所以,虽然我找到了一个可行的解决方案,但我想完全理解潜在的问题,我很感激进一步的解释。谢谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-03
    • 2020-12-06
    • 1970-01-01
    相关资源
    最近更新 更多