C# Нулевой распространяющий оператор / выражение условного доступа и блоки if

Распространяющий оператор Null / выражение условного доступа, появившееся в C#-6.0, выглядит довольно удобной функцией. Но мне любопытно, поможет ли это решить проблему проверки, не является ли дочерний элемент не нулевым, и затем вызова булева метода для указанного дочернего элемента внутри блока if:

  public class Container<int>{
       IEnumerable<int> Objects {get;set;}
  }

  public Container BuildContainer()
  { 
      var c = new Container();

      if (/* Some Random Condition */)
         c.Objects = new List<int>{1,2,4};
  }

  public void Test()
  {
      var c = BuildContainer();

      //Old way
      if ( null != c && null != c.Objects && c.Objects.Any())
         Console.Write("Container has items!");


      //C# 6 way?
      if (c?.Object?.Any())
          Console.Write("Container has items!");
  }

Будет c?.Object?.Any() компилировать? Если распространяющиеся операторы короткого замыкания (я полагаю, это правильный термин) к нулю, то у вас есть if (null), который не действителен.

Решит ли команда C# эту проблему или я пропустил предполагаемый вариант использования для оператора с нулевым распространением?

2 ответа

Решение

Это не сработает. Вы можете просто пропустить объяснение и увидеть код ниже:)

Как Вам известно ?. Оператор вернет ноль, если дочерний элемент нуль. Но что произойдет, если мы попытаемся получить ненулевой член, такой как Any() метод, который возвращает bool? Ответ заключается в том, что компилятор "обернет" возвращаемое значение в Nullable<>, Например, Object?.Any() даст нам bool? (который Nullable<bool>) не bool,

Единственное, что не позволяет нам использовать это выражение в if Утверждение, что оно не может быть косвенно приведено к bool, Но вы можете сделать сравнение явно, я предпочитаю сравнивать true как это:

if (c?.Object?.Any() == true)
    Console.Write("Container has items!");

Благодаря @DaveSexton есть еще один способ:

if (c?.Object?.Any() ?? false)
    Console.Write("Container has items!");

Но что касается меня, то сравнение с true кажется более естественным:)

Нулевой оператор вернул бы null или значение в конце выражения. Для типов значений он вернет результат в Nullable<T> так что в вашем случае это будет Nullabe<bool>, Если мы посмотрим на пример в документе для наступающих возможностей в C# (указан здесь), у него есть пример:

int? first = customers?[0].Orders.Count();

В приведенном выше примере вместо int, Nullable<int> будет возвращен. За bool это вернется Nullable<bool>,

Если вы попробуете следующий код в Visual Studio "14" CTP:

Nullable<bool> ifExist = c?.Objects?.Any();

Результатом вышеупомянутой строки будет Nullable<bool> / bool?, Позже вы можете сделать сравнение как:

Используя нуль-объединяющий оператор??

 if (c?.Object?.Any() ?? false)

С помощью Nullable<T>.GetValueOrDefault метод

if ((c?.Objects?.Any()).GetValueOrDefault())

Используя сравнение с true

if (c?.Objects?.Any() == true)

var x = c?.Objects?.Any() даст вам логическое значение, допускающее значение NULL, и, как говорили другие, это означает, что вы можете использовать оператор равенства, подобный этому

x == true

или вы можете использовать нулевое объединение, как это, чтобы ваш результат не был нулевым

var x = c?.Objects?.Any() ?? false

Но лично я считаю, что логические значения с 3 состояниями (допускающие значение NULL) - это запах кода. Даже если он практически невидим, его существование должно побудить вас задуматься о том, что вы на самом деле пытаетесь сделать, и определить, действительно ли логическое значение, допускающее значение NULL, действительно подходит. В этом случае я думаю, что вы действительно хотите сделать что-то вроде этого -

var objects = c?.Objects ?? Enumerable.Empty<Object>();
if (objects.Any())
{
 ...
}

Поместите это в метод расширения, и он будет еще лаконичнее -

public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection)
{
   return !(collection ?? Enumerable.Empty<T>()).Any()
}
Другие вопросы по тегам