В С# есть ли чистый способ проверки нескольких уровней нулевых ссылок

Например, если я хочу вызвать следующее: person.Head.Nose.Sniff() то, если я хочу быть в безопасности, я должен сделать следующее:

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

Есть ли более простой способ формулировки этого выражения?

Ответы

Ответ 1

Есть ли более простой способ формулировки этого выражения?

С С# 6 вы можете использовать оператор с нулевым условием ?.

Пример кода

Это ваш исходный код, упакованный в метод, и если 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;
    }

Это ваш код, переписанный оператором с нулевым условием С# 6:

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

?? - это оператор с нулевым коалесцированием и не связан с вашим вопросом.

В качестве полного примера см. https://github.com/lernkurve/Stackoverflow-question-3701563

Ответ 2

Сначала вы можете использовать короткое замыкание в логических логических операциях и сделать что-то вроде:

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

Также обратите внимание, что то, что вы делаете, противоречит руководящему принципу разработки программного обеспечения, известного как Закон Деметры.

Ответ 4

Не совсем, кроме

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

Ответ 5

Вы можете использовать null objects вместо нулевых значений. Sniff тогда ничего не сделает, если любые объекты в цепочке вызовов являются нулевыми объектами.

Это не вызовет исключения:

person.Head.Nose.Sniff(); 

Ваши нулевые классы могут выглядеть так (вы также можете использовать их как синглеты и иметь интерфейсы для IPerson, IHead и INose):

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; 

Ответ 7

Лучше всего использовать оператор && вместо вложенных операторов if:

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

Обратите внимание, что технически вы можете выполнить аналогичную нулевую проверку с помощью дерева выражений. У вашего метода будет такая подпись:

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

... который позволит вам написать код, выглядящий примерно так:

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

Но это будет связано с отражением и, как правило, намного сложнее следовать каким-либо углубленным образом по сравнению с методом &&.

Ответ 8

Я бы избавился от любого использования null и сделал что-то вроде этого:

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

Для этого потребуется какая-то база class или interface.

public abstract class BodyPart
{
    public bool IsDecapitated { get; private set; }

    public BodyPart(bool isDecapitated)
    {
        IsDecapitated = isDecapitated;
    } 
}