可能的重复:
C#中的安全导航算子
“如果对象为null,则为null;如果对象不为null,那么为object.member”
在我的XML处理项目中,我必须浏览chained属性以获得所需的值。例如CCD_ 1。并且该链中的任何对象都有可能为空。
我在谷歌上搜索了"c#中的NullSafe导航",发现了一些不错的文章。从一篇文章中,我得到了一个实现自定义扩展的想法。现在我有一个关于这个扩展的性能的问题。我有这3个解决方案。有人能建议我(就表现而言)采用哪一种最好吗?
-
选项1(使用本文中解释的逻辑):
//custom extension method public static TOutput IfNotNull<TInput, TOutput>(this TInput x, Func<TInput, TOutput> f) where TInput : class where TOutput : class { return x == null ? null : f(x); } //with custom extension method -- Very neat & clean.. but what about performance? string x = obj1 .IfNotNull(x => x.obj2) .IfNotNull(x => x.obj3) .IfNotNull(x => x.obj4) .IfNotNull(x => x.obj5) .IfNotNull(x => x.Value);
-
选项2:
//with NullCheck -- probably right way? if(obj1 != null && obj1.obj2 != null && obj1.obj2.obj3 != null && obj1.obj2.obj3.obj4 != null && obj1.obj2.obj3.obj4.obj5 != null) { string x = obj1.obj2.obj3.obnj4.obj5.Value; }
-
选项3:
//with try-catch.. With lowest cyclomatic complexity, but not a right approach. try { string x = obj1.obj2.obj3.obnj4.obj5.Value; } catch(NullReferenceException ne) { //ignore exception }
我肯定不会选择try-catch选项。这不仅是一种代码气味(异常驱动的开发),而且如果您担心性能,那么异常处理不是解决问题的方法。
我不太明白第二种选择。您是否必须将其放在要访问Value属性的任何位置?或者是在扩展方法中。
选项一看起来最干净。
关于性能:我认为您不会发现选项1和选项2之间有太大的差异,但您可以在一个小型控制台项目中尝试一下。只需运行第一个和第二个选项,例如1000次,然后计算所需的时间。不是精确的科学,但通常足以衡量绩效差异。
我猜你不会看到太大的区别。我觉得你在练习微观优化。除非你要在一个真正重要的系统上运行,否则选择对你来说最优雅的解决方案。
我支持选项#2。
选项#1:如果Obj1为null,则它将继续每次检查Obj2、Obj3、Obj4和Obj5是否为null!至少对于Option#2,只要它发现Obj1为null,就不会检查if语句的其余部分,这意味着处理周期更少。
选项#3当然不好。捕捉异常是一项开销,如果你反复遍历数千个节点,你会感觉到它——更不用说气味了。
我担心的是你可能问错了问题。你说你在使用XML,那么这些对象实际上就是元素,对吧?
也许,如果您用不同的措辞表达问题,并提供更多关于XML文档结构的信息,我们可以编写一个Linq查询来提取值,而不需要所有硬编码的null检查(以及我假设您也在使用的循环)。
你确定选项2这么糟糕吗?只要catch块不向调用者抛出任何异常,try/catch块就不会影响性能(异常抛出机制是性能消耗者)。
这里有一个引文:
寻找和设计异常严重的代码可能导致表现不错的胜利。请记住这与try/catch无关块:只有当实际的异常被抛出。你可以使用尽可能多的try/catch块你想要的。使用异常无偿是你失去的地方表演例如,您应该远离使用控制流的异常。
取自http://msdn.microsoft.com/en-us/library/ms973839.aspx
显然,只要你在链中的某个地方发现了一个null,设计#3就可以防止继续进行评估,就像设计#1一样,也可以防止像设计#2那样编写笨拙的代码。
我认为尝试/捕获的设计值得考虑。。。
我会使用一个节点结构,这样你就可以做到这一点:
var hasNullValue = false;
var x = string.Empty;
var node = firstNode;
while (node.Child != null)
{
// On the first hit we set the null flag
// and break out of the loop
if (node.Value == null)
{
hasNullvalue = true;
break;
}
node = node.Child;
}
if (!hasNullValue)
x = node.Value;