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

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

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

class Foo {
    public int? Measure;
}

Вот код, использующий оператор new.

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

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

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

Какие?

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

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

3 ответа

Решение

Давайте пройдемся по этому логически.

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

Мы не знаем, если f является нулевым или нет.

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

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

Теперь давайте пройдемся по этому:

var f = ???;
var i = f?.Measure.HasValue;
  1. Если f равно нулю, то результат (i) нулевой
  2. Если f не равно нулю, то результат (i) является Measure.HasValue, который является бул.

Следовательно, i это bool?,

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

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

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

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

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

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

Просто процитирую комментарий Роба:

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

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

Я столкнулся с этим сегодня.

Что печатается в следующем фрагменте кода C#?

public class NullTests
{
    public static void Main(string[] args)
    {
        object obj = DoIt();
        Console.WriteLine(obj?.ToString().NullToNothing());
    }

    private static object DoIt() => null;
}

public static class Extensions
{
    public static string NullToNothing(this string input) => input ?? "nothing";
}

Ответ: ноль.

Что печатается в следующем фрагменте кода Kotlin?

fun main() {
    val obj = doIt()
    println(obj?.toString().NullToNothing())
}

fun doIt() = null
fun String?.NullToNothing() = this ?: "nothing"

Ответ: "ничего".

Как и вы, я ожидал поведения Котлина, и большую часть дня это сбивало меня с толку.:(

Другие вопросы по тегам