Lukas92
Lukas92

Reputation: 363

Why does C# require parentheses when using nullables in an expression?

I'm new to C# and while exploring the language features, I came across something strange:

struct Foo
{
    public Foo Identity() { return this; }

    public static void Bar(Foo? foo)
    {
        Foo foo1 = foo?.Identity().Value; // Does not compile
        Foo foo2 = (foo?.Identity()).Value; // Compiles
    }
}

Could anyone explain to me why the parenthesis are needed?

Upvotes: 33

Views: 2044

Answers (2)

Zein Makki
Zein Makki

Reputation: 30022

I think it was a good decision from the c# team to do it this way. Consider the below scenario:

If the struct was :

struct Foo
{
    public int ID { set; get; }

    public Foo Identity() { return this; }

    public static void Bar(Foo? foo)
    {
        int? foo1 = foo?.Identity().ID; // compile
        Foo foo2 = (foo?.Identity()).Value; // Compiles
    }
}

If you didn't need the parenthesis to access the Nullable result, you wouldn't be able to access the ID property. Since the below will not compile:

int? foo2 = (foo?.Identity()).GetValueOrDefault()?.ID

When you write foo?.Identity(). what is after the . is of type Foo returned by Identity(). However in (foo?.Identity()). what is after the . is Foo? which is the actual result of the whole statement foo?.Identity().

Upvotes: 0

D Stanley
D Stanley

Reputation: 152511

Could anyone explain to me why the parenthesis are needed?

Because Identity() returns a Foo (not a Foo?) and thus has no Value property. If foo is null, the null will propagate through the Identity call.

When you put parentheses around it, the results of the expression is a Nullable<Foo> which does have a Value property.

Also note that if foo is null, then you will be calling Value on a Nullable<Foo> that has no value, and will get an exception at run-time. Some static analyzers will recognize that you have a possible null-reference exception waiting to happen and warn you.

If you expand them to their equivalents without null-propagation it will be more clear:

Foo foo1;
if(foo != null)
{
    foo1 = foo.Identity().Value;  // not possible - Foo has no Value property.
}
else
{
    foo1 = null;  // also not possible 
}

Foo foo2;
Foo? temp;
if(foo != null)
{
    temp = foo.Identity();
}
else
{
   temp = null;  // actually a Nullable<Foo> with no value
}
foo2 = temp.Value;  // legal, but will throw an exception at run-time if foo is null

If Identity() returns Foo, why does Foo foo3 = foo?.Identity(); not compile ?

The equivalent of that would be:

Foo foo3
if(foo != null)
{
    foo3 = foo.Identity();
}
else
{
    foo3 = null;  // not possible 
}

Upvotes: 40

Related Questions