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
является нулевым или нет.
- Если
f
равно нулю, то результат (i
) являетсяnull
- Если
f
не равно нулю, то результат (i
) являетсяint
Следовательно, i
определяется как int?
, а также t
это bool
Теперь давайте пройдемся по этому:
var f = ???;
var i = f?.Measure.HasValue;
- Если
f
равно нулю, то результат (i
) нулевой - Если
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"
Ответ: "ничего".
Как и вы, я ожидал поведения Котлина, и большую часть дня это сбивало меня с толку.:(