C #: вложенные условия против продолжения - PullRequest
16 голосов
/ 26 июля 2010

В последнее время при использовании ReSharper предлагается уменьшить вложенность в определенных местах путем инвертирования условий if и использования операторов continue.

вложенных условий:

foreach(....)
{
    if(SomeCondition)
    {
        //do some things

        if(SomeOtherNestedCondition)
        {
            //do some further things
        }
    }
}

операторы continue:

foreach(....)
{
    if(!SomeCondition) continue;

    //do some things

    if(!SomeOtherNestedCondition) continue;

    //do some further things
}

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

Какой подход вы предпочитаете и почему?Используете ли вы continue над вложенными if в вашем повседневном коде?

Ответы [ 10 ]

29 голосов
/ 26 июля 2010

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

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

Это не просто продолжение в циклах, это также относится к условиям методов, которые должны возвращаться;вместо метода start

if (valid)
{
    do stuff;
}

он всегда должен запускаться

if (notValid)
{
    return;
}
8 голосов
/ 26 июля 2010

Не должно быть существенной разницы в производительности, все дело в удобочитаемости.Лично я думаю, что последний легче читать.Меньше вложенности, легче читать.

6 голосов
/ 26 июля 2010

Краткий ответ:

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

Более длинный ответ:

I обычно предпочитают блоки с отступом предшествующим операторам "break" (continue, break, return или throw).

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

Для меня существует одно заметное исключение , а именно проверка аргументов в начале метода. Я форматирую этот код следующим образом:

if (thisArgument == null) throw new NullArgumentException("thisArgument");
if (thatArgument < 0) throw new ArgumentOutOfRangeException("thatArgument");
...

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

4 голосов
/ 26 июля 2010

с кодом стиля continue;как бы вы обрабатывали третью операцию, которая не зависит от SomeOtherNestedCondition, это делает порядок кода важным, что делает IMO менее обслуживаемым.

Например:

foreach(....) 
{ 
    if(!SomeCondition) continue; 

    //do some things 

    if(!SomeOtherNestedCondition) continue; 

    //do some further things 

    if(!SomeSecondNonNestedCondition) continue;

    // do more stuff
}

Что происходиткогда SomeOtherNestedCondition заставляет continue; произойти, но SomeSecondNonNestedCondition все еще должно выполняться?

Я бы рефакторинг каждого бита "кода" и использовать вложенные if() s для вызова каждого рефакторизованного метода, и я бы сохранилвложенная структура.

2 голосов
/ 26 июля 2010

Используйте комбинацию из двух.Я использую if(condition) continue; и if(condition) return; в верхней части цикла для проверки текущего состояния, а вложенные операторы if ниже, чтобы контролировать поток всего, что я пытаюсь выполнить.

2 голосов
/ 26 июля 2010

Простой ответ. Какой из них легче читать и понимать.

1 голос
/ 29 марта 2014

Оригинал continue значение: код не будет обработан, если какое-либо условие ложно.Вот пример ситуации:

class Program
{
    static bool SomeCondition = false;
    static bool SomeOtherNestedCondition = false;
    static void Main(string[] args)
    {    
        for (int i = 0; i < 2; i++)
        {
            if (SomeCondition)
            {
                //do some things

                if (SomeOtherNestedCondition)
                {
                    //do some further things
                }
            }
            Console.WriteLine("This text appeared from the first loop.");
        }
        for (int i = 0; i < 2; i++)
        {
            if (!SomeCondition) continue;

            //do some things

            if (!SomeOtherNestedCondition) continue;

            //do some further things
            Console.WriteLine("This text appeared from the second loop.");
        }
        Console.ReadLine(); 
    }
}

И вывод будет: enter image description here

1 голос
/ 26 июля 2010

Использование continue s делает основную часть кода на уровне обычного процедурного кода.Иначе, если есть пять проверок, вы сделаете отступ для «мяса» метода 10 или 20 символов, в зависимости от размера отступа, и это 10/20 символов, которые вам придется прокрутить, чтобы увидеть более длинные строки.

1 голос
/ 26 июля 2010

Нет разницы с точки зрения памяти или производительности. Базовый IL - это просто набор инструкций перехода / перехода, поэтому на самом деле не имеет значения, возвращаются ли они назад к началу цикла или к оператору "else".

Вы должны выбрать, что легче прочитать. Лично я предпочитаю избегать «продолжить», но если у вас много уровней вложенности, вам будет проще следовать вашему второму примеру.

0 голосов
/ 26 июля 2010

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

Ну, я думаю, это ваше личное мнение.Я, например, стараюсь избегать такого вложения, так как на мой взгляд код становится труднее читать.

Если вам нравится версия «до» больше, чем версия «после», используйте ее.Просто настройте ReSharper так, чтобы он подсказывал, что вы действительно хотите.

Всегда имейте в виду: ReSharper - довольно «тупой» инструмент рефакторинга - он не может заменить разработчика.Это может только помочь ему, выполняя какую-то глупую работу по копированию и вставке.Некоторые рефакторинги, выполненные ReSharper, приводят даже к ситуациям, когда ReSharper предлагает обратный рефакторинг.

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

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