Reputation: 363
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
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
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()
returnsFoo
, why doesFoo 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