Nullsafe在c#中导航

时间:2011-05-24 06:57:46

标签: c# .net linq null

  

可能重复:
  Safe Navigation Operator in C#?
  Shortcut for “null if object is null, or object.member if object is not null”

在我的XML处理项目中,我必须浏览链式属性才能获得所需的值。例如,obj1.obj2.obj3.obj4.obj....Value。并且可能会退出此链中的任何对象为空。

我搜索了“c#中的NullSafe导航”并找到了一些不错的文章。从其中一个Post,我有了实现自定义扩展的想法。 现在我对这个扩展的性能有疑问。 我有这3个解决方案。任何人都可以建议我采用哪一种(在性能方面)?

  • 选项1(使用logic explained on this article):

    //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;
    }
    
  • 2选项:

    //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
    }
    

4 个答案:

答案 0 :(得分:3)

我绝对不会选择try-catch选项。这不仅是代码嗅觉(异常驱动的开发),而且如果你担心性能,异常处理也不是可行的方法。

我不太了解第二种选择。您是否必须将它放在您想要访问Value属性的任何位置?或者是扩展方法。

选项一看起来最干净。

关于性能:我认为您不会发现选项1和2之间存在很大差异,但您可以在一个小型控制台项目中尝试一下。只需运行第一个和第二个选项,比如1000次并计算所需的时间。不是精确的科学,但通常足以衡量绩效差异。

我猜你不会看到很大的区别。我在想你正在练习micro-optimization。除非你要在一个真正重要的系统上运行,否则请选择对你来说最优雅的解决方案。

答案 1 :(得分:1)

我选择#2。

选项#1:如果Obj1为空,那么它每次都会继续为Obj2,Obj3,Obj4和Obj5检查是否为空!至少在选项#2中,一旦发现Obj1为空,它就不会检查if语句的其余部分 - 这意味着更少的处理周期。

选项#3当然很糟糕。捕获异常是开销,如果你复发地经历数千个节点,你会感觉到它 - 更别提气味。

我担心你可能会问错误的问题。你说你正在使用XML,那么这些对象真的是元素,对吗?

也许如果你以不同的方式表达你的问题并提供有关XML文档结构的更多信息,我们可以编写一个Linq查询来提取值而不需要所有硬编码的空值检查(并且我假设你也是循环使用)。

答案 2 :(得分:1)

您是否确定选项2如此糟糕? 只要catch块没有向调用者抛出任何异常(它是性能消耗的异常抛出机制),try / catch块不会影响性能。

这是一个引用:

  

寻找和设计   异常繁重的代码可能导致a   体面的胜利。请记住这一点   这与try / catch无关   块:你只会产生成本   抛出实际异常。您   可以使用尽可能多的try / catch块   你要。使用例外   无偿的是你失败的地方   性能。例如,你应该   远离使用的东西   控制流程的例外。

取自http://msdn.microsoft.com/en-us/library/ms973839.aspx

显然,设计#3可以防止继续进行评估,只要你在设计#1中的链中找到一个null,并且还可以防止像设计#2那样繁琐的代码编写。

我认为try / catch设计值得考虑......

答案 3 :(得分:0)

我会使用节点结构,这样你就可以这样做:

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;