Null условный оператор не работает с типами NULL?
Я пишу кусок кода в С# 6 и по какой-то странной причине это работает
var value = objectThatMayBeNull?.property;
но это не так:
int value = nullableInt?.Value;
Не работает я имею в виду, что я получаю ошибку компиляции, говоря Cannot resolve symbol 'Value'
.
Любая идея, почему нулевой условный оператор ?.
не работает?
Ответы
Ответ 1
Хорошо, я немного подумал и испытал. Вот что происходит:
int value = nullableInt?.Value;
Дает это сообщение об ошибке при компиляции:
Тип 'int' не содержит определения для `Value '
Это означает, что ?
"преобразует" int?
в фактическое значение int
. Это фактически то же самое, что:
int value = nullableInt ?? default(int);
В результате получается целое число, которое не имеет Value
, очевидно.
Хорошо, может ли это помочь?
int value = nullableInt?;
Нет, этот синтаксис не разрешен.
Так что же тогда? Просто продолжайте использовать .GetValueOrDefault()
для этого случая.
int value = nullableInt.GetValueOrDefault();
Ответ 2
Причиной этого является то, что доступ к значению с помощью оператора с нулевым условием был бы бессмысленным:
- Когда вы применяете
x?.p
, где p
- тип значения с нулевым значением T
, результат имеет тип T?
. Точно так же результат операции nullableInt?.Value
должен быть нулевым.
- Когда ваш
Nullable<T>
имеет значение, результат nullableInt?.Value
будет таким же, как и само значение
- Если ваш
Nullable<T>
не имеет значения, результат будет null
, что опять же совпадает с самим значением.
Хотя не имеет смысла обращаться к Value
с оператором ?.
, имеет смысл получить доступ к другим свойствам типов значений с нулевым значением. Оператор работает с нулевыми типами значений и ссылочными типами, поэтому эти две реализации производят одинаковое поведение:
class PointClass {
public int X { get; }
public int Y { get; }
public PointClass(int x, int y) { X = x; Y = y; }
}
struct PointStruct {
public int X { get; }
public int Y { get; }
public PointStruct(int x, int y) { X = x; Y = y; }
}
...
PointClass pc = ...
PointStruct? ps = ...
int? x = pc?.X;
int? y = ps?.Y;
В случае nullable struct
оператор позволяет вам получить доступ к свойству базового типа PointStruct
, и он добавляет значение null к результату таким же образом, как и для непустых свойств ссылочного типа PointClass
.
Ответ 3
Что касается типов с нулевым значением, оператор ?.
говорит if not null, use the wrapped value
. Итак, для nullable int, если значение nullable имеет значение 8
, результатом ?.
будет 8
, а не значение nullable, содержащее 8
. Поскольку Value
не является свойством int
, вы получаете сообщение об ошибке.
Итак, пример попытки использования свойства Value
вполне справедливо терпит неудачу, но следующее будет работать,
var x = nullableInt?.ToString();
Рассмотрим оператор нуль-коалесцирования, ??
.
var x = nullableInt ?? 0;
Здесь оператор говорит, if null, return 0, otherwise return the value inside the nullable
, который в этом случае равен int
. Оператор ?.
выполняет аналогичные действия с целью извлечения содержимого с нулевым значением.
В вашем конкретном примере вы должны использовать оператор ??
и соответствующий по умолчанию, а не оператор ?.
.
Ответ 4
Я в основном согласен с другими ответами. Я просто надеялся, что наблюдаемое поведение может быть подкреплено какой-то официальной документацией.
Так как я не могу найти спецификацию С# 6.0 где-нибудь (еще нет?), наиболее близким к "документации" является С# Language Design Примечания за 3 февраля 2014 года. Предполагая, что найденная там информация все еще отражает текущее состояние дел, вот соответствующие части, которые формально объясняют наблюдаемое поведение.
Семантика подобна тому, как применить тернарный оператор к проверке нулевого равенства, нулевому литералу и не подверженному вопросительному запросу оператору, за исключением того, что выражение оценивается только один раз:
e?.m(…) => ((e == null) ? null : e0.m(…))
e?.x => ((e == null) ? null : e0.x)
e?.$x => ((e == null) ? null : e0.$x)
e?[…] => ((e == null) ? null : e0[…])
Где e0
совпадает с e
, , за исключением того, что e
имеет тип значения NULL, в этом случае e0
равен e.Value
.
Применение этого последнего правила к:
nullableInt?.Value
... семантически эквивалентное выражение становится:
((nullableInt == null) ? null : nullableInt.Value.Value)
Ясно, что nullableInt.Value.Value
не может скомпилировать и что вы наблюдали.
Что касается того, почему было принято конструктивное решение применить это специальное правило к типам с нулевым типом, я думаю, что dasblinkenlight
ответ покрывает это красиво, поэтому я не буду повторять его здесь.
Кроме того, я должен упомянуть об этом, даже если, предположительно, у нас не было этого специального правила для типов с нулевым значением, а выражение nullableInt?.Value
выполнялось и велось так, как вы изначально думали...
// let pretend that it actually gets converted to this...
((nullableInt == null) ? null : nullableInt.Value)
следующий оператор из вашего вопроса будет недействительным и приведет к ошибке компиляции:
int value = nullableInt?.Value; // still would not compile
Причина, по которой он все равно не будет работать, заключается в том, что тип выражения nullableInt?.Value
будет int?
, а не int
. Поэтому вам нужно будет изменить тип переменной value
на int?
.
Это также официально описано в С# Language Design Notes для 3 февраля 2014 г.:
Тип результата зависит от типа T
правой стороны базового оператора:
- Если
T
является (как известно,) ссылочным типом, тип выражения равен T
- Если
T
является (как известно,) типом непустого значения, тип выражения равен T?
- Если
T
является (как известно,) типом с нулевым значением, тип выражения равен T
- В противном случае (то есть, если неизвестно, является ли
T
ссылочным или типом значения) выражение является ошибкой времени компиляции.
Но если вы затем будете вынуждены написать следующее, чтобы скомпилировать его:
int? value = nullableInt?.Value;
... тогда это кажется довольно бессмысленным, и это не будет отличаться от простого:
int? value = nullableInt;
Как указывали другие, в вашем случае вы, вероятно, хотели использовать оператор с нулевым коалесцентом ??
, а не оператор с нулевым условием ?.
.
Ответ 5
Оператор с нулевым условием также разворачивает переменную с нулевым значением. Итак, после "?". оператор, свойство "Значение" больше не требуется.
Я написал сообщение, в котором подробно описывается, как я натолкнулся на это. Если вам интересно,
http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/
Ответ 6
Просто потому, что (на основе ответа sstan выше)
var value = objectThatMayBeNull?.property;
оценивается компилятором как
var value = (objectThatMayBeNull == null) ? null : objectThatMayBeNull.property
и
int value = nullableInt?.Value;
как
int value = (nullableInt == null) ? null : nullableInt.Value.Value;
когда nullableInt.Value.Value
является ошибкой синтаксиса Cannot resolve symbol 'Value'
!
Ответ 7
int
не имеет свойства Value
.
Рассмотрим:
var value = obj?.Property
Является эквивалентным:
value = obj == null ? null : obj.Property;
Это не имеет смысла с int
и, следовательно, не с int?
через ?.
Старый GetValueOrDefault()
имеет смысл с int?
.
Или, если это так, поскольку ?
должен вернуть что-то нулевое, просто:
int? value = nullableInt;