С# Безопасный навигационный оператор - что на самом деле происходит?

Я с интересом следил за функцией безопасного навигатора, добавленной в С# 6. Я с нетерпением жду этого какое-то время. Но я нахожу другое поведение, чем я ожидал. Я понимаю, что я действительно не понимаю, как это работает.

Учитывая этот класс

class Foo {
    public int? Measure;
}

Вот некоторый код с использованием нового оператора.

Foo f = new Foo { Measure = 3 };
Console.WriteLine(f?.Measure);  // 3

f = new Foo { Measure = null };
Console.WriteLine(f?.Measure);  // null

f = null;
Console.WriteLine(f?.Measure);  // null

До сих пор все работает как ожидалось. ?. обращается к членам, когда левая сторона не равна нулю, в противном случае возвращается значение null. Но здесь все идет в направлении, которого я не ожидал.

var i = f?.Measure; // i is Nullable<int>
Console.WriteLine(i.HasValue); // false
Console.WriteLine(f?.Measure.HasValue); // null

Что?

Почему я могу получить HasValue из i, но не из того же выражения, которое я присвоил i? Как HasValue когда-либо быть нулевым?

Изменить: мой реальный вопрос касается поведения программы, а не ошибки компиляции. Я удалил лишний материал о компиляции и более подробно остановился на этом вопросе, почему два разных результата возвращаются по той же логике.

Ответ 1

Проследите это логически.

var f = ???;
var i = f?.Measure;
var t = i.HasValue;

Мы не знаем, является ли f нулевым или нет.
1. Если f null, то результат (i) равен null
2. Если f не null, то результатом (i) является int

Следовательно, i определяется как int?, а t - это bool

Теперь, пройдем через это:

var f = ???;
var i = f?.Measure.HasValue;
  • Если f - null, то результат (i) равен null
  • Если f не null, то результатом (i) является Measure.HasValue, что является bool.

Следовательно, i является bool?.

Если f равно null, мы коротко замыкаем и возвращаем null. Если это не так, мы возвращаем результат bool .HasValue.

По существу, при использовании ?. - возвращаемый тип должен быть опорным значением или Nullable<T>, так как выражение может коротко замыкаться, чтобы вернуть значение null.

Ответ 2

Nullable<T> на самом деле является структурой и поэтому не может быть нулевым, только его Value может, поэтому HasValue всегда будет доступен.

Ответ 3

var i = f?.Measure; // i is Nullable<int>
Console.WriteLine(i.HasValue); // false
Console.WriteLine(f?.Measure.HasValue); // null

В этом случае f равно null.

Причина, по которой i.HasValue возвращает false, потому что i имеет тип Nullable<int>. Поэтому даже когда значение i равно null, как и в этом случае, i.HasValue все еще доступен.

Тем не менее, f?.Measure.HasValue сразу возвращает null после оценки f?. Следовательно, результат вы видите выше.

Просто чтобы процитировать комментарий Роба:

Главное, чтобы понять, что вы читаете и понимаете это: f?.Measure.HasValue как это: (f?.Measure).HasValue, который он нет.