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

Оператор Null-условный вычисляет bool, чтобы не bool? как и ожидалось

Я только что обновился с VS 2010 до 2015 года. Мне нравится новый оператор с нулевым условием, который также известен как нулевое распространение, Это позволяет упростить код, например:

string firstCustomerName = customers?[0].Name; // null if customers or the first customer is null

другой:

int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null

который возвращает Nullable<int>, даже если Enumerable.Count возвращает int, чтобы разделить действительный счетчик и любой nulls раньше. Это довольно интуитивно и очень полезно.

Но зачем это компилируется и работает как ожидалось (он возвращает false):

string text = null;
bool contains = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase) >= 0;

Он должен либо вернуть bool? (которого он не делает), либо не компилировать.

4b9b3361

Ответ 1

На самом деле у вас есть

string text = null;
int? index = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = index >= 0;

и int? >= int является совершенно законным.

Причина, по которой она была разделена, документация для оператора гласит: "Если одна операция в цепочке условного доступа к члену и операция индекса возвращает значение null, то остальные остановки цепочки. Другие операции с более низким приоритетом в выражении продолжаются". Это означает, что .? будет оценивать вещи с одинаковым приоритетом или выше, прежде чем "создаст значение".

Если вы посмотрите порядок приоритета оператора, вы увидите, что "Операторы реляционного и типового тестирования" в списке значительно ниже, поэтому значение будет создано до применения >=.


ОБНОВЛЕНИЕ:. Поскольку он был поднят в комментариях, вот раздел спецификации С# 5 о том, как ведут себя теги >= и другие операторы при работе с нулевым значением. Я не смог найти документ для С# 6.

7.3.7 Поднятые операторы

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

  • Для унарных операторов     + ++ - -- ! ~

    существует снятая форма оператора, если типы операндов и результатов являются неинифицируемыми типами значений. Поднятая форма построенный путем добавления сингла? модификатор в операнд и результат типы. Выбранный оператор создает нулевое значение, если операнд ноль. В противном случае снятый оператор разворачивает операнд, применяет основного оператора и обертывает результат.

  • Для двоичных операторов
    + - * / % & | ^ << >>

    существует снятая форма оператора, если операнд и типы результатов все типы значений, не подлежащие обнулению. Поднятая форма построена по добавив сингл? модификатора для каждого операнда и типа результата. Поднятые оператор генерирует нулевое значение, если один или оба операнда равны нулю ( исключение - это и и | операторы bool? типа, как описано в §7.11.3). В противном случае снятый оператор разворачивает операнды, применяет базовый оператор и обертывает результат.

  • Для операторов равенства == !=

    существует поднятая форма оператора, если типы операндов оба типы с нулевым значением и если тип результата равен bool. Поднятые форма строится путем добавления сингла? модификатора для каждого операнда тип. Поднятый оператор считает два нулевых значения равными, а нулевой значение не равно любому ненулевому значению. Если оба операнда не равны нулю, снятый оператор разворачивает операнды и применяет основные оператора для получения результата bool.

  • Для реляционных операторов < > <= >=

    существует допустимая форма оператора, если типы операндов являются неинифицируемыми типами значений, и если тип результата равен bool. Поднятые форма строится путем добавления сингла? модификатора для каждого операнда тип. Поднятый оператор выдает значение false, если один или оба операнды равны нулю. В противном случае снятый оператор разворачивает операнды и применяет основной оператор для получения результата bool.

Ответ 2

Если вы используете этот код и наведите указатель мыши на x, вы увидите, что x - это int?:

var x = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = x >= 0;

Таким образом, типизация верна.

Затем рассмотрим x >= 0: это int? >= int. По-видимому, существует оператор между обнуляемыми и не нулевыми структурами. Вот почему он работает.

Если вы посмотрите на IL, вы увидите, что на самом деле он вызывает HasValue и GetValueOrDefault(). Я предполагаю, что оператор делает это, но я не смог найти его в исходном источнике, поэтому он должен быть в CLR или компиляторе:

instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()

...
instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()