Подтвердить что ты не робот

Null условный оператор не работает с типами NULL?

Я пишу кусок кода в С# 6 и по какой-то странной причине это работает

var value = objectThatMayBeNull?.property;

но это не так:

int value = nullableInt?.Value;

Не работает я имею в виду, что я получаю ошибку компиляции, говоря Cannot resolve symbol 'Value'. Любая идея, почему нулевой условный оператор ?. не работает?

4b9b3361

Ответ 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;