下面的代码可以简化吗?
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实现类似的逻辑是可能的?有什么想法吗?
发布于 2014-03-04 16:14:28
在您的代码中,变量结果只是object
类型,因为编译器不知道FirstOrDefault
的结果将是什么。
下面我使用了显式类型,使用T
来提高清晰度,这样var
就不会计算为object
我创建了这个扩展方法,这样您就不必每次想要使用对象时都强制转换它.它会在返回之前处理好。
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代码
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
语句的另一个可能的实现。
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# 零传播的一个新特性,我们可以将所有这些调用串在一起。现在,我们不再需要保存链中的每个对象(除非我们愿意)。
下面是一个使用空传播的实现。
object finalResult = ((model.FirstOrDefault<ExpandoObject>("node")
?.FirstOrDefault<KeyValuePair<string, object>>("children").Value as IList<object>)
?.FirstOrDefault() as ExpandoObject)
?.FirstOrDefault<object>("stuff");
发布于 2014-03-04 14:57:17
FirstOrDefault
是LINQ扩展方法(参见扩展对象扩展方法),所以您正在使用LINQ!
@Ryan的答案看起来是用代码来解决结构问题,得到了我的+1。
我想补充的是,您应该避免为不同的含义重用相同的变量。
这对我来说很难判断,但是看起来代码是在滥用dynamic
--代码编译和运行是因为result
的类型是dynamic
,因此在运行时解析了它,但是您正在利用每一个机会将它转换回一个ExpandoObject
,这样您就可以获得您正在使用的类型的编译时知识。
这样做将“削减”dynamic
,并让var
从一开始就直接计算为ExpandoObject
:
var result = model.FirstOrDefault(x => x.Key == "node").Value as ExpandoObject;
然后你可以简单地做:
if (result != null)
{
result = result.FirstOrDefault(x => x.Key == "children");
然而,不断地将result
重新分配到不同的值无助于可读性,我建议您为每一步设置一个有意义的名称。
发布于 2014-03-04 14:31:39
弹出的第一件事是重复的调用,看起来如下:
结果=((ExpandoObject)结果).FirstOrDefault(x => x.Key == "node").Value;
它们都是相似的。你一半的电话以.Value
结束,另一半没有。这就引出了我的一个问题,因为我对C#中的var
不太了解。所以,你可以把它放在一个单独的函数中:
/**
* 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) { ... }
经过进一步检查,我们也可以稍微改进这一点。
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的结构,在这种情况下,我几乎总是使用递归方法来查找要查找的子节点。在我的经验中,您可以通过传递对象与节点队列一起搜索来实现这一点。这里唯一的缺点是在特定的地方寻找特定的数据,所以您必须知道所有的步骤才能到达那里(在本例中是这样做的)。
// 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
。
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;
}
然后问题就变成了,我们如何称呼所有这些来使它发挥作用?我将从代码的第一行开始:
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#中显式地命名事物的类型。dynamic
和var
是很棒的,也有他们的位置,但我只把它们用在一行上。在重新查看此代码时,通过嵌套语句进行很长时间的var
调试和维护可能会很痛苦。
不幸的是,我对linq知之甚少--嗯,我知道,但我还没有充分利用它,不知道何时以及如何有效地使用它--所以我不能提供一个使用linq的解决方案。
https://codereview.stackexchange.com/questions/43400
复制相似问题