Условный оператор Null для "уничтожения" существования элемента массива
Новый оператор с нулевым условием С# 6.0 - удобный инструмент для написания более сжатого и менее сложного кода. Предполагая, что у одного есть массив клиентов, тогда вы можете получить null вместо длины, если customers
имеет значение null, используя это (примеры из MSDN):
int? length = customers?.Length;
Аналогично вы можете получить null вместо клиента с этим:
Customer first = customers?[0];
И для более сложного выражения это дает null, если customers
равно null, первый клиент имеет значение null, или первый клиент Orders
объект имеет значение null:
int? count = customers?[0]?.Orders?.Count();
Но тогда есть интересный случай несуществующего клиента, который, по-видимому, не выполняет оператор с нулевым условием. Мы видели выше, что пустой клиент покрыт, то есть если запись в массиве customers
равна нулю. Но это совершенно отличается от несуществующего клиента, например. ищет клиента 5
в 3-элементном массиве или клиенте n
в списке из 0 элементов. (Обратите внимание, что это же обсуждение относится и к поиску словаря.)
Мне кажется, что оператор с нулевым условием сосредоточен исключительно на отрицании эффектов исключения NullReferenceException; IndexOutOfRangeException или KeyNotFoundException - одни, разоблачены, сжимаются в углу и нуждаются в том, чтобы сами себя заботиться! Я утверждаю, что в духе оператора с нулевым условием он должен также иметь возможность обрабатывать эти случаи..., что приводит к моему вопросу.
Я пропустил это? Предоставляет ли нуль-условность какой-либо элегантный способ по-настоящему покрыть это выражение...
customers?[0]?.Orders?.Count();
... когда нет нулевого элемента?
Ответы
Ответ 1
Нет, потому что это null -словный оператор, а не indexoutofrange -словный оператор и является просто синтаксическим сахаром для следующего:
int? count = customers?[0]?.Orders?.Count();
if (customers != null && customers[0] != null && customers[0].Orders != null)
{
int count = customers[0].Orders.Count();
}
Вы можете видеть, что если нет нулевого клиента, вы получите свой обычный IndexOutOfRangeException
.
Один из способов, которыми вы могли бы обойти это, - иметь метод расширения, который проверяет индекс и возвращает null, если он не существует:
public static Customer? GetCustomer(this List<Customer> customers, int index)
{
return customers.ElementAtOrDefault(index); // using System.Linq
}
Тогда ваш чек может быть:
int? count = customers?.GetCustomer(0)?.Orders?.Count();
Ответ 2
customers?.FirstOrDefault()?.Orders?.Count();
Нет нулей, никаких проблем.
Ответ 3
Он не поддерживает безопасность индексирования, потому что, когда вы переходите к нему, индексист действительно является просто синтаксическим сахаром для любого другого типа метода.
Например:
public class MyBadArray
{
public Customer this[int a]
{
get
{
throw new OutOfMemoryException();
}
}
}
var customers = new MyBadArray();
int? count = customers?[5]?.Orders?.Count();
Если это поймать здесь? Что делать, если исключение было более разумным, похожее на KeyNotFoundException, но специфичным для типа коллекции, которую мы реализуем? Мы должны постоянно обновлять функциональность ?.
, чтобы не отставать.
Кроме того, ?.
не использует исключения. Это предотвращает их.
var customer = customers?[5];
фактически скомпилирован как:
Customer customer = null;
if (customers != null)
customer = customers[5];
Устранение исключений становится исключительно сложным. Например:
void Main()
{
var thing = new MyBadThing();
thing.GetBoss()?.FireSomeone();
}
public class MyBadThing
{
public class Boss
{
public void FireSomeone()
{
throw new NullReferenceException();
}
}
public Boss GetBoss()
{
return new Boss();
}
}
Если бы это было просто перехватывание исключений, оно было бы записано как:
Boss boss = customer.GetBoss();
try
{
boss.FireSomeone();
} catch (NullReferenceException ex) {
}
Который фактически поймал бы исключение в пределах FireSomeone
, а не исключение ссылочной ссылки, которое было бы выбрано, если boss был null.
Такая же проблема с плохими ошибками будет присутствовать, если мы хотим поймать исключения для поиска индекса, исключить ключевые исключения и т.д.
Ответ 4
Если вы хотите получить n-й элемент без исключений NullReference или IndexOutOfRange, вы можете использовать:
customers?.Skip(n)?.FirstOrDefault()