, >, or even ==. Despite this, the following code compiles and executes correctly: int? x = 5;
int? y = 10;
bool b = x < y; // true
This works because the compiler steals or “lifts” the less-than operator from the underlying value type. Semantically, it translates the preceding comparison expression into this:
bool b = (x.HasValue && y.HasValue) ? (x.Value < y.Value) : false;
In other words, if bothxandyhave values, it compares viaint’s less-than operator; otherwise, it returnsfalse.
Operator lifting means you can implicitly useT’s operators onT?. You can define operators forT?in order to provide special-purpose null behavior, but in the vast majority of cases, it’s best to rely on the compiler automatically applying systematic nullable logic for you. Here are some examples:
int? x = 5;
int? y = null;
// equality operator examples
Console.WriteLine(x == y); // false
Console.WriteLine(x == null); // false
Console.WriteLine(x == 5); // true
Console.WriteLine(y == null); // true
Console.WriteLine(y == 5); // false
Console.WriteLine(y != 5); // true
// relational operator examples
Console.WriteLine(x < 6); // true
Console.WriteLine(y < 6); // false
Console.WriteLine(y > 6); // false
// all other operator examples
Console.WriteLine(x + 5); // 10
Console.WriteLine(x + y); // null (prints empty line)
The compiler performs null logic differently depending on the category of operator. The following sections explain these different rules.
Equality operators (== !=)
The equality operators work on the principle that the behavior for nullable types works exactly as it does for reference types. This means a nonnull value is not equal to a null value, but two null values are equal.
bool a = x == y; // translation:
bool a = (x != null && y != null) ? (x.Value == y.Value) :
(x != null ^ y != null);
// a is true
Relational operators (< <= >= >)
The relational operators work on the principle that it is meaningless to compare null operands. This means comparing a null value to either a null or nonnull value returns false.
false .The relational operators work on the principle that it is meaningless to compare null operands. This means comparing a null value to either a null or nonnull value returns false.
bool b = x < y; // translation:
bool b = (x == null || y == null) ? false : (x.Value < y.Value);
// b is false
All other operators (+ - * / % & | ^ << >> + ++ - -- ! ~)
These operators work on the principle to always return “I don’t know” (i.e., null) when fed any operands that are null. This means that if any operand is null, the result is also null. This pattern should be familiar to SQL users.
int? c = x + y; // translation:
int? c = (x == null || y == null) ? null : (int?)(x.Value + y.Value);
// c is null
Next: Mixing nullable and nonnullable operators >>
More C# Articles
More By O'Reilly Media