4

I'm confused about how the null-conditional operator cascades with normal property access. Take these two examples:

a?.b.c
(a?.b).c

I would expect them to be equivalent: first, the value of a?.b is evaluated, then result.c is evaluated. Thus if a == null, an exception should be thrown.

However, that only happens in the second expression. The first expression evaluates to null, meaning it's the same as a?.b?.c. Why?

10
  • 2
    This is the safe-navigation-operator. Commented Aug 1, 2018 at 18:58
  • 2
    Nope, it stops at ?. if a is null. The second one ends up telling it you definitely want to evaluate c. Commented Aug 1, 2018 at 18:59
  • 3
    @FrankerZ In C# it's called the null conditional operator. Commented Aug 1, 2018 at 18:59
  • 2
    @FrankerZ The official docs call it the null-conditional operator, which is why I used that term. Commented Aug 1, 2018 at 18:59
  • 3
    @BlueRaja-DannyPflughoeft The important part to notice in the docs is The null-conditional operators are short-circuiting. If one operation in a chain of conditional member access and index operation returns null, then the rest of the chain’s execution stops Commented Aug 1, 2018 at 19:01

2 Answers 2

9

That's only a matter of operator precedence. Let's go through the cases:

a?.b.c

  1. Evaluate a => null is returned, nothing else is evaluated given that the null-conditional operators are short-circuiting.

(a?.b).c

  1. Evaluate a => null is returned
  2. Evaluate ((B)null).c => NullReferenceException is thrown

For these cases to be equivalent, you should be comparing

  1. a?.b.c
  2. (a?.b)?.c
  3. a?.b?.c (as you already mentioned)
Sign up to request clarification or add additional context in comments.

Comments

1

I don't know if this'll help or not, beyond what Camilo already provided, but here's a brief comparison showing how the logic might look without the null-conditional operator in play.

public class Program
{
    public static void Main()
    {
        A a;

        a = new A { b = new B { c = 5 } };

        Console.WriteLine(a?.b.c);        // returns 5;
        Console.WriteLine((a?.b).c);      // returns 5;

        a = null;

        Console.WriteLine(a?.b.c ?? -1);  // returns -1;
        Console.WriteLine((a?.b).c);      // throws NullReferenceException


        // Similar to a?.b.c

        if (a != null)
            Console.WriteLine(a.b.c);     // returns 5;
        else
            Console.WriteLine(-1);        // returns -1;


        // Similar to (a?.b).c

        B tmp;
        if (a != null)
            tmp = a.b;
        else
            tmp = null;

        Console.WriteLine(tmp.c);         // returns 5 or throws NullReferenceException
    }
}


public class A
{
    public B b { get; set; }
}

public class B
{
    public int c { get; set; }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.