Запрос на упрощение кода C #: нулевой контейнер и цикл по каждому элементу - PullRequest
12 голосов
/ 14 марта 2009

У меня часто есть код, который выглядит примерно так:

if (itm != null)
{
    foreach (type x in itm.subItems())
    {
        //dostuff
    }
}
//do more stuff

В ситуациях, когда //do more stuff опущен, очень легко избежать дополнительного цикла foreach. Выход из области действия с использованием соответствующей команды (в зависимости от того, что происходит, обычно означает выражение return или continue statement).

Этот тип вещи приводит к появлению кода стрелки. В настоящее время у меня есть несколько способов справиться с этим:

  • Используйте код как itm = itm == null ? itm.subItems() : emptyArray
  • Разрешить код стрелки
  • Использование goto
  • Использовать злые хакерские взломы (завернуть все, если утверждение во всех, в область видимости, а затем вырваться из нее). По моему мнению, злые хакерские взломы в основном эквивалентны goto, за исключением более уродливых и трудных для чтения, поэтому я не считаю это правильным решением.
  • Переведите некоторые куски в новые методы. На самом деле, есть несколько случаев, когда это является хорошим решением, но в большинстве случаев оно не подходит, поскольку нулевые ссылки в основном являются ошибочными состояниями из MS-функций.

Кто-нибудь хочет дать ответ о том, какие подходы считаются предпочтительными?

Ответы [ 4 ]

15 голосов
/ 14 марта 2009

Если вы используете C # 3, вы всегда можете написать метод расширения:

public static IEnumerable<SubItem> SafeSubItems(this ItemType item)
{
     return item == null ? Enumerable.Empty<SubItem> : source.SubItems();
}

Тогда просто напишите:

foreach (SubItem x in itm.SafeSubItems())
{
    // do stuff
}
// do more stuff

Ключевым моментом является то, что методы расширения могут вызываться даже "на" нулевых ссылках.

Что было бы неплохо, так это оператор «разыменования с нулевой безопасностью», поэтому мы могли бы написать:

// Not valid C# code!
foreach (SubItem x in itm?.SubItems() ?? Enumerable.Empty<SubItem>())
{
}

Или просто определите EmptyIfNull метод расширения для IEnumerable<T> и используйте

// Not valid C# code!
foreach (SubItem x in (itm?.SubItems()).EmptyIfNull())
{
}
3 голосов
/ 14 марта 2009

Вы можете использовать оператор Coalesce (кодируется как двойной знак вопроса, ??, .net 2 вверх). Это вернет первое ненулевое значение в списке значений, поэтому в этом фрагменте ...

MyClass o1 = null;
MyClass o2 = new MyClass ();
MyClass o3 = null;
return o1 ?? o2 ?? o3;

... o2 будет возвращено.

Чтобы вы могли перекодировать исходный пример кода как

foreach (type x in (itm ?? emptyArray).subItems())
{
    //dostuff
}

//do more stuff

Впрочем, лично я не против вложенности. Это сразу понятно, что происходит. Мне кажется, что оператор Coalesce немного сложнее читать, и это маленькое гнездо - небольшая цена за ясность.

2 голосов
/ 14 марта 2009

Мне нравится меньше вложенности, для меня это читается лучше. Нет, пожалуйста, пожалуйста:)

Я держу методы короткими, так что обычно это возвращение для этого сценария.

if (itm == null) return;
foreach (type x in itm.subItems())
{
   //dostuff
}

Если вам нужно больше вещей, которые являются простыми утверждениями и могут быть выполнены до foreach, вы можете:

if (itm == null)
{
   //do more stuff 
   return;
}
foreach (type x in itm.subItems())
{
   //dostuff
}

Если вышеприведенное не так, скорее всего, метод слишком длинный, и часть его все равно будет удалена. Возможно:

if( itm != null ) SomeActionOnSubItems(itm.subItems);
// do more stuff (can be some method calls depending on level of abstraction).
0 голосов
/ 14 марта 2009

Лично я бы, вероятно, оставил конструкцию такой, какая у вас есть.

Первый вариант (itm = itm == null? Itm.subItems (): emptyArray) кажется менее неприятным, чем другие, но мне все равно нравится ваш оригинальный.

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

Выполнение любого из хаков, чтобы избежать проверки if, кажется, что вы пытаетесь быть слишком умным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...