C#: Вложенные условные выражения по сравнению с продолжают оператор

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

вложенные условные выражения:

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

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

продолжите операторы:

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

    //do some things

    if(!SomeOtherNestedCondition) continue;

    //do some further things
}

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

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

16
задан KP. 26 July 2010 в 19:13
поделиться

9 ответов

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

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

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

if (valid)
{
    do stuff;
}

он должен всегда начинаться

if (notValid)
{
    return;
}
29
ответ дан 30 November 2019 в 15:29
поделиться

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

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

1
ответ дан 30 November 2019 в 15:29
поделиться

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

8
ответ дан 30 November 2019 в 15:29
поделиться

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

1
ответ дан 30 November 2019 в 15:29
поделиться

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

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

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

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

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

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

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

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

6
ответ дан 30 November 2019 в 15:29
поделиться

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

2
ответ дан 30 November 2019 в 15:29
поделиться

С кодом в стиле 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()ы для вызова каждого рефакторизованного метода, и я бы сохранил вложенную структуру.

4
ответ дан 30 November 2019 в 15:29
поделиться

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

2
ответ дан 30 November 2019 в 15:29
поделиться

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

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

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

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

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

BTW: Здесь вы должны руководствоваться соображениями производительности - я буду удивлен, если есть действительно заметные различия в производительности.

0
ответ дан 30 November 2019 в 15:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: