繁体   English   中英

在C#中,如何在递归遍历ExpandoObject时更改键值属性的值?

[英]In C#, how do I change a Key-Value-Property's value while recursively traversing an ExpandoObject?

问题

使用C#,我需要遍历从XML ExpandoObject转换为ExpandoObject的对象,并用新值替换所有“ price”属性。

该对象非常不结构化,具有多层嵌套节点(实际上是嵌套的ExpandoObjects)。 更具体地说,层次结构可能如下所示:

产品=>价格,数量,配件

每个配件可能有价格和数量,并且本身可能有配件,这就是为什么我需要递归。

到目前为止我有什么

  public ExpandoObject UpdatePricing(ExpandoObject exp)
    {
        //Is there a price property?
        var hasPrice = exp.Any(a => a.Key == "price");
        if (hasPrice)
        {
            //update price here
            exp.price = 0; //Some other price
        }

        //Now loop through the whole object.  If any of the properties contain an expando, then call this method again
        foreach (var kvp in exp)
        {
            if (kvp.Value is ExpandoObject)
            {
                //THIS CODE IS NO GOOD BECAUSE kvp.Value has no setter!!
                kvp.Value = UpdatePricing(kvp.Value);
            }
        }

        return exp;
    }

我遇到的问题是kvp.Value没有设置器,因此我无法递归运行此方法。

任何建议表示赞赏。 谢谢!

我对ExpandoObject不太了解。 但是,与大多数字典实现一样,我假设通常如果希望将键值对更新为具有不同的值,则需要通过字典接口。

请注意,在枚举字典内容时,您(可能)不会被修改。 因此,您需要构建一个元素列表以进行更新,并在单独的操作中进行操作。

例如:

List<string> keysToUpdate = new List<string>();

foreach (var kvp in exp)
{
    if (kvp.Value is ExpandoObject)
    {
        keysToUpdate.Add(kvp.Key);
    }
}

foreach (string key in keysToUpdate)
{
    exp[key] = UpdatePricing(exp[key]);
}

您也可以将整个KeyValuePair值保留在列表中,以避免第二次检索该值,但是我猜想这并不是一个重要的优化。

由于ExpandoObject实现IDictionary<string, Object>会变得容易一些。 我们也可以将返回类型更改为void因为我们不需要重新分配结果。

void UpdatePrice(ExpandoObject expando, decimal price)
{
    var map = (IDictionary<string, Object>)expando;
    if (map.ContainsKey("price"))
        map["price"] = price;
    foreach (var value in map.Values) 
    {
        if (value is ExpandoObject)
            UpdatePrice((ExpandoObject)value, price);
    }
}

我对此进行了一些测试,并通过使expando动态化来使其工作:

    public static ExpandoObject DoWork(ExpandoObject obj)
    {
        dynamic expando = obj;

        //update price
        if (obj.Any(c => c.Key == "price"))
            expando.price = 354.11D;

        foreach (var item in expando)
        {
            if (item.Value is ExpandoObject)
            {
                //call recursively
                DoWork(item.Value);
            }
        }
        return expando;
    }

它限制了类型安全性,但是根据MSDN实际上,动态似乎是与expandos交互的最佳方式,反正是您没有奢侈的地方:

“在C#中,要为ExpandoObject类的实例启用后期绑定,必须使用dynamic关键字。有关更多信息,请参见使用Type dynamic(C#编程指南)。”

这意味着,如果不使用dynamic关键字,则将在CLR中运行Expando而不是DLR,这将产生一些奇怪的后果,例如无法设置值。 希望这会有所帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM