I basically agree with the other answers. I just hoped that the observed behavior could be backed up by some kind of official documentation.
Since I cannot find the C # 6.0 specification anywhere (not yet?), The closest to the โdocumentationโ is the C # Language Design Notes for February 3, 2014 . Assuming that the information found there still reflects the current state of affairs, here are the relevant parts that formally explain the observed behavior.
The semantics are similar to how to apply a ternary operator to checking for null equality, a null literal, and an operator not subject to a query query, except that the expression is evaluated only once:
e?.m(โฆ) => ((e == null) ? null : e0.m(โฆ)) e?.x => ((e == null) ? null : e0.x) e?.$x => ((e == null) ? null : e0.$x) e?[โฆ] => ((e == null) ? null : e0[โฆ])
Where e0 matches e , except that e is NULL, in this case e0 is e.Value .
Applying this last rule to:
nullableInt?.Value
... the semantically equivalent expression becomes:
((nullableInt == null) ? null : nullableInt.Value.Value)
It is clear that nullableInt.Value.Value cannot compile and what you have observed.
Regarding why the constructive decision was made to apply this special rule to types with null type, I think the dasblinkenlight answer covers this beautifully, so I wonโt repeat it here.
In addition, I should mention this, even if, presumably, we did not have this special rule for types with a null value, and the expression nullableInt?.Value executed and was carried out as you originally thought ...
// let pretend that it actually gets converted to this... ((nullableInt == null) ? null : nullableInt.Value)
the following statement from your question will be invalid and will result in a compilation error:
int value = nullableInt?.Value;
The reason it won't work anyway is because the expression type nullableInt?.Value will be int? , not int . So you will need to change the type of the variable value to int? .
This is also officially described in C # Language Design Notes for February 3, 2014 :
The type of result depends on the type T right side of the base operator:
- If
T is (as you know) a reference type, the type of the expression is T - If
T is (as you know) a type of non-empty value, is the type of expression equal to T? - If
T is (as you know) a type with a zero value, the type of the expression is T - Otherwise (that is, if it is not known whether
T reference or a value type), the expression is a compile-time error.
But if you are then forced to write the following to compile it:
int? value = nullableInt?.Value;
... then this seems rather pointless, and it will not differ from simple:
int? value = nullableInt;
As others pointed out, in your case, you probably wanted to use a zero-coalescent operator ?? , but not an operator with a null condition ?. .