Pitfalls and Best Practices to prevent them #2: Operator priority

Example

class Program
{
    static void Main(string[] args)
    {
        Foo foo = new Foo();
        foo.Test = "Baz";

        foo.Bar(null);
    }
}

public sealed class Foo
{
    public string Test
    {
        get;
        set;
    }

    public void Bar(Foo other)
    {
        this.Test += "Value:" + other == null ? "<null>" : other.Test;
    }
}

What does it do?

The method Bar contains an expression using the tertiary operator to handle the case that other is null.

In this case “<null>” is appended instead of the value of other.Test.

However, this is only what the code should do. In fact, there occurs a NullReferenceException.

Doku says

C# Language Specification (7.2.1: Operator precedence and associativity):

The following table summarizes all operators in order of precedence from highest to lowest:

Section Category Operators
7.5 Primary x.y f(x) a[x] x++ x– new typeof default checked unchecked delegate
7.6 Unary + – ! ~ ++x –x (T)x
7.7 Multiplicative * / %
7.7 Additive + –
7.8 Shift << >>
7.9 Relational and type testing < > <= >= is as
7.9 Equality == !=
7.10 Logical AND &
7.10 Logical XOR ^
7.10 Logical OR |
7.11 Conditional AND &&
7.11 Conditional OR ||
7.12 Null coalescing ??
7.13 Conditional ?:
7.16, 7.14 Assignment and lambda expression = *= /= %= += -= <<= >>= &= ^= |= =>

 

Problem

I already mentioned: There occurs a NullReferenceException.

Let’s take a closer look to the problematic line:

this.Test += "Value:" + other == null ? "<null>" : other.Test;

As the += operator has a lower precedence than the others, we can say: The following part is executed before the assignment:

"Value:" + other == null ? "<null>" : other.Test;

What about this part now? Well, as the +-operator has a higher precedence than the ?:-operator, we are now able to write what the compiler actually does in another way:

string temp = "Value:" + other; //Will always be "Value:ConsoleApplication1.Foo"
temp = temp == null ? "<null>" : other.Test; //As temp is never null, temp will be other.Test
this.Test += temp;

You should see the problem in the second line now: other.Test is accessed irrespective of its “nullstate”.

How to solve?

As always (:-)) there is a simple solution: Make clear what you want by using brackets:

this.Test += "Value:" + (other == null ? "<null>" : other.Test);

The code not only gets “right” now, it’s also easier to understand.

Generalization / Best Practice:

In (complex) statements, do not hesitate to use brackets; better “safe than sorry”. They make the code clearer and prevent errors originating in misjudged operator precedence. Two very important cases are the ?:-operator and all logical operators.

DotNetKicks Image
Advertisements
Posted in Pitfalls. Tags: , , . Comments Off on Pitfalls and Best Practices to prevent them #2: Operator priority