深度检查,是否有更好的方法?

深度检查,是否有更好的方法?

我们都去过那里,我们有像cake.frosting.berries.loader这样的深层属性,我们需要检查它是否为空,所以没有例外。要做的是使用短路if语句

if (cake != null && cake.frosting != null && cake.frosting.berries != null) ...

这不是很优雅,也许应该有一种更简单的方法来检查整个链,看看它是否出现了null变量/属性。

是否可以使用某种扩展方法或者它是一种语言功能,还是只是一个坏主意?


波斯汪
浏览 484回答 3
3回答

泛舟湖上清波郎朗

我们考虑过添加新的操作“?”。到具有您想要的语义的语言。(现在已添加;见下文。)也就是说,你会说cake?.frosting?.berries?.loader并且编译器会为您生成所有短路检查。它没有成为C#4的标准。也许是对于该语言的假设未来版本。更新(): 该?.运营商正在计划在未来罗斯林编译器版本。请注意,对运算符的确切语法和语义分析仍存在争议。更新(): Visual Studio 2015已经发布,并附带一个C#编译器,支持null条件运算符?.和?[]。

MM们

我受到了这个问题的启发,试图找出如何使用表达式树更简单/更漂亮的语法来完成这种深度空值检查。虽然我同意答案,如果你经常需要访问层次结构中的深层实例,那么它可能是一个糟糕的设计,我也认为在某些情况下,例如数据表示,它可能非常有用。所以我创建了一个扩展方法,允许你写:var&nbsp;berries&nbsp;=&nbsp;cake.IfNotNull(c&nbsp;=>&nbsp;c.Frosting.Berries);如果表达式的任何部分都不为null,则返回Berries。如果遇到null,则返回null。但是有一些注意事项,在当前版本中它只能用于简单的成员访问,它只适用于.NET Framework 4,因为它使用了MemberExpression.Update方法,这是v4中的新方法。这是IfNotNull扩展方法的代码:using&nbsp;System;using&nbsp;System.Collections.Generic;using&nbsp;System.Linq.Expressions;namespace&nbsp;dr.IfNotNullOperator.PoC{ &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;class&nbsp;ObjectExtensions &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;TResult&nbsp;IfNotNull<TArg,TResult>(this&nbsp;TArg&nbsp;arg,&nbsp;Expression<Func<TArg,TResult>>&nbsp;expression) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(expression&nbsp;==&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;ArgumentNullException("expression"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ReferenceEquals(arg,&nbsp;null)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;default(TResult); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;stack&nbsp;=&nbsp;new&nbsp;Stack<MemberExpression>(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;expr&nbsp;=&nbsp;expression.Body&nbsp;as&nbsp;MemberExpression; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(expr&nbsp;!=&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stack.Push(expr); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expr&nbsp;=&nbsp;expr.Expression&nbsp;as&nbsp;MemberExpression; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(stack.Count&nbsp;==&nbsp;0&nbsp;||&nbsp;!(stack.Peek().Expression&nbsp;is&nbsp;ParameterExpression)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;ApplicationException(String.Format("The&nbsp;expression&nbsp;'{0}'&nbsp;contains&nbsp;unsupported&nbsp;constructs.", &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expression)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;object&nbsp;a&nbsp;=&nbsp;arg; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(stack.Count&nbsp;>&nbsp;0) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expr&nbsp;=&nbsp;stack.Pop(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;p&nbsp;=&nbsp;expr.Expression&nbsp;as&nbsp;ParameterExpression; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(p&nbsp;==&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;=&nbsp;Expression.Parameter(a.GetType(),&nbsp;"x"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expr&nbsp;=&nbsp;expr.Update(p); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;lambda&nbsp;=&nbsp;Expression.Lambda(expr,&nbsp;p); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Delegate&nbsp;t&nbsp;=&nbsp;lambda.Compile();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;t.DynamicInvoke(a); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ReferenceEquals(a,&nbsp;null)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;default(TResult); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(TResult)a;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;}}它的工作原理是检查表达你的表达式的表达式树,并一个接一个地评估这些部分;&nbsp;每次检查结果都不为空。我确信这可以扩展,以便支持除MemberExpression之外的其他表达式。将其视为概念验证代码,请记住,使用它会导致性能下降(在许多情况下可能无关紧要,但不要在紧密循环中使用它:-))

潇湘沐

我发现这个扩展对于深度嵌套场景非常有用。public&nbsp;static&nbsp;R&nbsp;Coal<T,&nbsp;R>(this&nbsp;T&nbsp;obj,&nbsp;Func<T,&nbsp;R>&nbsp;f) &nbsp;&nbsp;&nbsp;&nbsp;where&nbsp;T&nbsp;:&nbsp;class{ &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;obj&nbsp;!=&nbsp;null&nbsp;?&nbsp;f(obj)&nbsp;:&nbsp;default(R);}这是我从C#和T-SQL中的空合并运算符中获得的一个想法。好处是返回类型始终是内部属性的返回类型。这样你可以这样做:var&nbsp;berries&nbsp;=&nbsp;cake.Coal(x&nbsp;=>&nbsp;x.frosting).Coal(x&nbsp;=>&nbsp;x.berries);...或上述的略有变化:var&nbsp;berries&nbsp;=&nbsp;cake.Coal(x&nbsp;=>&nbsp;x.frosting,&nbsp;x&nbsp;=>&nbsp;x.berries);这不是我所知道的最好的语法,但确实有效。
打开App,查看更多内容
随时随地看视频慕课网APP