I basically agree with the other answers. I was just hoping that the observed behavior could be backed up by some form of authoritative documentation.
Since I can't find the C# 6.0 specification anywhere (is it out yet?), the closest I found to "documentation" are the C# Language Design Notes for Feb 3, 2014. Assuming the information found in there still reflects the current state of affairs, here are the relevant parts that formally explain the observed behavior.
The semantics are like applying the ternary operator to a null equality check, a null literal and a non-question-marked application of the operator, 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 is the same as e, except if e is of a nullable value type, in which case e0 is e.Value.
Applying that last rule to:
nullableInt?.Value
... the semantically equivalent expression becomes:
((nullableInt == null) ? null : nullableInt.Value.Value)
Clearly, nullableInt.Value.Value can't compile, and that's what you observed.
As to why the design decision was made to apply that special rule to nullable types specifically, I think dasblinkenlight's answer covers that nicely, so I won't repeat it here.
Additionally, I should mention that, even if, hypothetically, we didn't have this special rule for nullable types, and the expression nullableInt?.Value did compile and behave as you originally thought...
// let's pretend that it actually gets converted to this...
((nullableInt == null) ? null : nullableInt.Value)
still, the following statement from your question would be invalid and produce a compilation error:
int value = nullableInt?.Value; // still would not compile
The reason why it would still not work is because the type of the nullableInt?.Value expression would be int?, not int. So you would need to change the type of the value variable to int?.
This is also formally covered in the C# Language Design Notes for Feb 3, 2014:
The type of the result depends on the type T of the right hand side of the underlying operator:
- If
T is (known to be) a reference type, the type of the expression is T
- If
T is (known to be) a non-nullable value type, the type of the expression is T?
- If
T is (known to be) a nullable value type, the type of the expression is T
- Otherwise (i.e. if it is not known whether
T is a reference or value type) the expression is a compile time error.
But if you would then be forced to write the following to make it compile:
int? value = nullableInt?.Value;
... then it seems pretty pointless, and it wouldn't be any different from simply doing:
int? value = nullableInt;
As others have pointed out, in your case, you probably meant to use the null-coalescing operator ?? all along, not the null-conditional operator ?..
??operator in this situation. As innullableInt ?? 0. - Jeff Yates