首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在ExpandoObject中查找元素

在ExpandoObject中查找元素
EN

Code Review用户
提问于 2014-03-04 12:53:09
回答 5查看 39.2K关注 0票数 15

下面的代码可以简化吗?

代码语言:javascript
运行
复制
dynamic model = GetExpandoObject(); //model type of ExpandoObject

var result = model.FirstOrDefault(x => x.Key == "node").Value;

if (result != null)
{
    result = ((ExpandoObject)result).FirstOrDefault(x => x.Key == "children");

    if (result != null)
    {
        result = ((IList<object>)((KeyValuePair<string, object>)result).Value).FirstOrDefault();

        if (result != null)
        {
            if (result != null)
            {
                result = ((ExpandoObject)result).FirstOrDefault(x => x.Key == "node").Value;

                if (result != null)
                {
                    result = ((ExpandoObject)result).FirstOrDefault(x => x.Key == "stuff");
                }
            }
        }
    }
}

也许用Linq实现类似的逻辑是可能的?有什么想法吗?

EN

回答 5

Code Review用户

回答已采纳

发布于 2014-03-04 16:14:28

在您的代码中,变量结果只是object类型,因为编译器不知道FirstOrDefault的结果将是什么。

下面我使用了显式类型,使用T来提高清晰度,这样var就不会计算为object

我创建了这个扩展方法,这样您就不必每次想要使用对象时都强制转换它.它会在返回之前处理好。

代码语言:javascript
运行
复制
public static class ExtensionMethods
{
    public static T FirstOrDefault<T>(this ExpandoObject eo, string key)
    {
        object r = eo.FirstOrDefault(x => x.Key == key).Value;
        return (r is T) ? (T)r : default(T);
    }
}

下面是使用上述方法重新实现原始post代码

代码语言:javascript
运行
复制
ExpandoObject model = GetExpandoObject();

var someExpandoObject = model.FirstOrDefault<ExpandoObject>("node");
if (someExpandoObject == null) return;

var kvp = someExpandoObject.FirstOrDefault<KeyValuePair<string, object>>("children");
if (kvp.Equals(default(KeyValuePair<string, object>))) return;

var ilo = kvp.Value as IList<object>;
if (ilo == null) return;

someExpandoObject = ilo.FirstOrDefault() as ExpandoObject;
if (someExpandoObject == null) return;

someExpandoObject = someExpandoObject.FirstOrDefault<ExpandoObject>("node");
if (someExpandoObject == null) return;

object finalResult = someExpandoObject.FirstOrDefault<object>("stuff");

下面是使用嵌套if语句而不是多个return语句的另一个可能的实现。

代码语言:javascript
运行
复制
ExpandoObject model = GetExpandoObject();

ExpandoObject someExpandoObject;
KeyValuePair<string, object> kvp;
IList<object> ilo;
object finalResult;

if ((someExpandoObject = model.FirstOrDefault<ExpandoObject>("node")) != null)
{
    if (!(kvp = someExpandoObject.FirstOrDefault<KeyValuePair<string, object>>("children")).Equals(default(KeyValuePair<string, object>)))
    {
        if ((ilo = kvp.Value as IList<object>) != null)
        {
            if ((someExpandoObject = ilo.FirstOrDefault() as ExpandoObject) != null)
            {
                if ((someExpandoObject = someExpandoObject.FirstOrDefault<ExpandoObject>("node")) != null)
                    finalResult = someExpandoObject.FirstOrDefault<object>("stuff");
            }
        }
    }
}

更新:使用C# 零传播的一个新特性,我们可以将所有这些调用串在一起。现在,我们不再需要保存链中的每个对象(除非我们愿意)。

下面是一个使用空传播的实现。

代码语言:javascript
运行
复制
object finalResult = ((model.FirstOrDefault<ExpandoObject>("node")
    ?.FirstOrDefault<KeyValuePair<string, object>>("children").Value as IList<object>)
    ?.FirstOrDefault() as ExpandoObject)
    ?.FirstOrDefault<object>("stuff");
票数 8
EN

Code Review用户

发布于 2014-03-04 14:57:17

FirstOrDefault是LINQ扩展方法(参见扩展对象扩展方法),所以您正在使用LINQ!

@Ryan的答案看起来是用代码来解决结构问题,得到了我的+1。

我想补充的是,您应该避免为不同的含义重用相同的变量。

这对我来说很难判断,但是看起来代码是在滥用dynamic --代码编译和运行是因为result的类型是dynamic,因此在运行时解析了它,但是您正在利用每一个机会将它转换回一个ExpandoObject,这样您就可以获得您正在使用的类型的编译时知识。

这样做将“削减”dynamic,并让var从一开始就直接计算为ExpandoObject

代码语言:javascript
运行
复制
var result = model.FirstOrDefault(x => x.Key == "node").Value as ExpandoObject;

然后你可以简单地做:

代码语言:javascript
运行
复制
if (result != null)
{
    result = result.FirstOrDefault(x => x.Key == "children");

然而,不断地将result重新分配到不同的值无助于可读性,我建议您为每一步设置一个有意义的名称。

票数 5
EN

Code Review用户

发布于 2014-03-04 14:31:39

弹出的第一件事是重复的调用,看起来如下:

结果=((ExpandoObject)结果).FirstOrDefault(x => x.Key == "node").Value;

它们都是相似的。你一半的电话以.Value结束,另一半没有。这就引出了我的一个问题,因为我对C#中的var不太了解。所以,你可以把它放在一个单独的函数中:

代码语言:javascript
运行
复制
/**
 *  You'll have to fill in the return type and come up with a super
 * cool name.
 */
var getObj(String key)
{
    return ((ExpandoObject)result).FirstOrDefault(x => x.Key == key);
}

// Combine with an assignment in the condition statement, and
// you can do this:
if((result = getObj(key)) != null) { ... }

经过进一步检查,我们也可以稍微改进这一点。

代码语言:javascript
运行
复制
bool getObj(String key, out var result)
{
    result = ((ExpandoObject)result).FirstOrDefault(x => x.Key == key);
    return result != null;
}

// So you can do things like:
if(getObj(key, out result)) { ... }

弹出的第二件事是五个嵌套的if语句。我以前听过的经验法则是最多有2-3个嵌套循环/ifs。看起来您正在遍历类似XML的结构,在这种情况下,我几乎总是使用递归方法来查找要查找的子节点。在我的经验中,您可以通过传递对象与节点队列一起搜索来实现这一点。这里唯一的缺点是在特定的地方寻找特定的数据,所以您必须知道所有的步骤才能到达那里(在本例中是这样做的)。

代码语言:javascript
运行
复制
// I'm making some assumptions here.
ExpandoObject find(Queue<String> queue, ExpandoObject tree)
{
    if(getObj(queue.Dequeue, out tree))
    {
        return find(queue, tree);
    }
    else if (queue.Count > 0)
    {
        // We couldn't find an intermediate node, so we want 
        //to return null to show our failure
        return null; 
    }
    else 
    {
        return tree; // May be null!
    }
}  

最后,IList似乎受到了阻碍。它不同于您正在进行的其他函数调用,因为它使用的是节点的Value而不是它的Key。然而,如果我们重新讨论我们的getObj函数,我们可能能够解决这个问题。如下面所示,如果我们传递一个与""相等的键,我们不会查找键,而是将结果设置为Value

代码语言:javascript
运行
复制
bool getObj(String key, out var result)
{
    if(key.Equals(""))// If there is no key, get the value
    {
        result = ((IList<object>)((KeyValuePair<string, object>)result).Value).FirstOrDefault();
    }
    else // If there is a key, use it!
    {
        result = ((ExpandoObject)result).FirstOrDefault(x => x.Key == key);
    }
    return result != null;
}

然后问题就变成了,我们如何称呼所有这些来使它发挥作用?我将从代码的第一行开始:

代码语言:javascript
运行
复制
dynamic model = GetExpandoObject(); //model type of ExpandoObject

Queue<String> queue;
queue.Enqueue("node");
queue.Enqueue("");
queue.Enqueue("children");
queue.Enqueue("");
queue.Enqueue("node");
queue.Enqueue("");
queue.Enqueue("stuff");

ExpandoObject result = find(queue, model);
if(result != null)
{
    // Success! do what you will with it.
}

当然,要注意的是,我不能编译其中任何一个,不能运行,也不能验证其中任何一个的正确性。至少,我希望它给你一些想法,或会为你工作,通过一些调整。

我个人倾向于总是在C#中显式地命名事物的类型。dynamicvar是很棒的,也有他们的位置,但我只把它们用在一行上。在重新查看此代码时,通过嵌套语句进行很长时间的var调试和维护可能会很痛苦。

不幸的是,我对linq知之甚少--嗯,我知道,但我还没有充分利用它,不知道何时以及如何有效地使用它--所以我不能提供一个使用linq的解决方案。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/43400

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档