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()
}