Your MSDN quote echoes §4.1.10 of the C# 5.0 specification:
A nullable type is written T?
, where T
is the underlying type. This syntax is shorthand for System.Nullable<T>
, and the two forms can be used interchangeably.
But “interchangeably” is an oversimplification. It’s true that T?
means System.Nullable<T>
, but as you’ve discovered, you can’t use T?
everywhere that you can use System.Nullable<T>
. In particular, the kind of member-access (§7.6.4) in your example requires a simple-name (§7.6.2):
[§7.6] Primary expressions include the simplest forms of expressions.
primary-expression:
primary-no-array-creation-expression
array-creation-expression
primary-no-array-creation-expression
literal
simple-name
parenthesized-expression
member-access
...
[§7.6.2] A simple-name is either of the form I
or of the form I<A1, ..., AK>
, where I
is a single identifier and <A1, ..., AK>
is an optional type-argument-list.
[§7.6.4] A member-access is either of the form E.I
or of the form E.I<A1, ..., AK>
, where E
is a primary-expression, I
is a single identifier and <A1, ..., AK>
is an optional type-argument-list.
Nullable<T>
is a simple-name and T?
isn’t, so the former compiles whereas the latter doesn’t.
Why did the C# language designers require a member-access expression to use a simple-name as opposed to any type? I suppose only they can say for sure, but maybe this requirement simplified the grammar: In an expression, the compiler can assume that ?
is always the conditional (ternary) operator instead of possibly a nullable type specifier.
In hindsight though, this was a fortunate choice that allowed C# 6.0 to add the ?.
operator without possibly breaking existing programs. For example, consider this pathological example:
struct S
{
public bool Equals(int x, int y) { return false; }
}
class C
{
public static void Main()
{
S? S = new S();
Console.WriteLine(S?.Equals(1, 1)); // "True" or "False"?
}
}
Should S?.Equals
be parsed as Nullable<S> . Equals
, a call to the Equals
static method of class Object
? Or should it be parsed as S ?. Equals
, a null-conditional call to the Equals
instance method of variable S
? Because S?
isn’t a simple-name, it’s unambiguously the latter.