【问题标题】:In C#, is there a clean way of checking for multiple levels of null references在 C# 中,是否有一种干净的方法来检查多个级别的空引用
【发布时间】:2010-09-13 14:55:00
【问题描述】:

例如,如果我想调用以下代码: person.Head.Nose.Sniff() 那么,如果我想安全,我必须做到以下几点:

if(person != null)
    if(person.Head != null)
        if(person.Head.Nose != null)
            person.Head.Nose.Sniff();

有没有更简单的方法来表达这个表达式?

【问题讨论】:

  • 不是每个Person 都有一个Head 并且每个Head 都有一个Nose 吗?
  • 世界上仍有地方实行斩首……
  • ...在这种情况下,Person 将变为 Corpse
  • ..或者只是让它的IsCorpse 属性变为真
  • 你也可以使用 Null Object Pattern ...这将要求每个人都有一个 Head,即使它不是他们自己的。

标签: c# null reference-type


【解决方案1】:

首先,您可以利用布尔逻辑运算符中的短路并执行以下操作:

if (person != null && person.Head != null && person.Head.Nose != null)
{
    person.Head.Nose.Sniff();
}

另请注意,您所做的事情违反了开发软件的设计指南,即Law of Demeter

【讨论】:

  • 同意。在这种情况下,我可能会考虑创建一个方法 person.Sniff() 调用 Head.Sniff() (如果不是 null)调用 Nose.Sniff( ) (如果不为空)。这样,您只需要检查 person 是否不为空,并按照 Demeter 法则的设计将知识最小化给朋友。
  • “我希望它被称为 Demeter 的偶尔有用的建议。” Martin Fowler haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx
【解决方案2】:

有没有更简单的方法来表达这个表达式?

对于 C# 6,您可以使用 null-conditional operator ?

代码示例

这是您打包到方法中的原始代码,并假设 Sniff() 总是返回 true

    public bool PerformNullCheckVersion1(Person person)
    {
        if (person != null)
            if (person.Head != null)
                if (person.Head.Nose != null)
                    return person.Head.Nose.Sniff();
        return false;
    }

这是用 C# 6 空条件运算符重写的代码:

    public bool PerformNullCheckVersion2(Person person)
    {
        return person?.Head?.Nose?.Sniff() ?? false;
    }

??null-coalescing operator,与您的问题无关。

有关完整示例,请参阅: https://github.com/lernkurve/Stackoverflow-question-3701563

【讨论】:

    【解决方案3】:

    这是另一个类似 Fluent Parameter Validation 的实现:Chained null checks and the Maybe monad

    【讨论】:

      【解决方案4】:

      其实不然

       if (person != null && person.Head != null && person.Head.Nose != null) 
      

      【讨论】:

        【解决方案5】:

        您可以使用null objects 代替空值。如果调用链中的任何对象为空对象,Sniff 将不执行任何操作。

        这不会引发异常:

        person.Head.Nose.Sniff(); 
        

        您的 null 类可能如下所示(您也可以将它们用作单例并具有IPersonIHeadINose 的接口):

        class NullPerson : Person {
          public override Head Head { get { return new NullHead(); }
        }
        class NullHead : Head {
          public override Nose Nose { get { return new NullNose(); }
        }
        class NullNose : Nose {
          public override void Sniff() { /* no-op */ }
        }
        

        作为旁注,在 Oxygene 中有一个 operator 用于此:

        person:Head:Nose:Sniff; 
        

        【讨论】:

        • 我考虑过发布这个答案......我只是想不出一个很好的理由来拥有null 正文部分。
        • @ChaosPandion:哈哈!这个例子对这个讨论真的很糟糕!也许是 Default 身体部分,永远不要让它为空。
        【解决方案6】:

        【讨论】:

        • 这看起来很有趣。在 vb.net 中,属性不能在不被读取或写入的情况下被引用,因此可以将大多数验证方法更改为属性以确保执行 Check()。不确定在 C 中是否有任何方法可以做到这一点。我不喜欢使用 null 对象的方法或属性,但是静态虚拟实例应该和“null”一样工作。顺便说一句,系统确实允许定义一个继承 Exception 的泛型类;这样的类可以被抛出和捕获。进一步继承是一种痛苦,但并非不可能。
        【解决方案7】:

        最好的方法就是使用&& 运算符而不是嵌套的if 语句:

        if (person != null && person.Head != null && person.Head.Nose != null)
        {
            person.Head.Nose.Sniff();
        }
        

        请注意,您在技术上可以执行类似的空检查using an expression tree。你的方法会有这样的签名:

        static bool IsNotNull<T>(Expression<Func<T>> expression);
        

        ...这将允许您编写如下所示的代码:

        if (IsNotNull(() => person.Head.Nose))
        {
            person.Head.Nose.Sniff();
        }
        

        但这会涉及到反思,并且与&amp;&amp; 方法相比,通常更难以任何深入的方式遵循。

        【讨论】:

          【解决方案8】:
          if (person?.Head?.Nose != null) person.Head.Nose.Sniff();   
          

          【讨论】:

          • 为什么不person?.Head?.Nose?.Sniff(); ? (我认为这会起作用)
          【解决方案9】:

          我会摆脱对null 的任何使用,并做这样的事情:

          ((Nose)person.BodyParts[BodyPart.Nose]).Sniff();
          

          这需要某种基础classinterface

          public abstract class BodyPart
          {
              public bool IsDecapitated { get; private set; }
          
              public BodyPart(bool isDecapitated)
              {
                  IsDecapitated = isDecapitated;
              } 
          }
          

          【讨论】:

            猜你喜欢
            • 2011-09-07
            • 1970-01-01
            • 2011-06-25
            • 2014-10-17
            • 1970-01-01
            • 2011-07-27
            • 1970-01-01
            • 2014-10-26
            • 1970-01-01
            相关资源
            最近更新 更多