Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the null-conditional operator change regular property access?

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?

like image 413
BlueRaja - Danny Pflughoeft Avatar asked Dec 23 '22 05:12

BlueRaja - Danny Pflughoeft


2 Answers

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)
like image 78
Camilo Terevinto Avatar answered Feb 16 '23 01:02

Camilo Terevinto


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; }
}
like image 42
Grant Winney Avatar answered Feb 15 '23 23:02

Grant Winney