在C#中,如何在递归遍历ExpandoObject时更改Key-Value-Property的值?

时间:2014-10-17 23:16:16

标签: c# recursion expandoobject idictionary dynamicobject

问题

使用C#,我需要遍历从ExpandoObject投射到XML的对象,并用新值替换任何“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没有setter,所以我不能递归地运行这个方法。

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

3 个答案:

答案 0 :(得分:2)

由于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);
    }
}

答案 1 :(得分:1)

我对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值保留在列表中,以避免第二次检索值,但我猜测这不是一个重要的优化。

答案 2 :(得分:1)

我刚刚对此进行了一些测试,并且能够通过让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;
    }

它可以消除类型安全,但看起来你似乎没有那种奢侈,动态是与{expand}实际上根据MSDN进行交互的最佳方式:

  

&#34;在C#中,为ExpandoObject的实例启用后期绑定   class,你必须使用dynamic关键字。有关更多信息,请参阅   使用Type dynamic(C#编程指南)。&#34;

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