Подтвердить что ты не робот

С#: Вложенные условные выражения vs continue

При использовании 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 для вложенных ifs в свой ежедневный код?

4b9b3361

Ответ 1

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

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

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

if (valid)
{
    do stuff;
}

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

if (notValid)
{
    return;
}

Ответ 2

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

Ответ 3

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

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

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

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

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

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

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

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

Ответ 4

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

Ответ 5

Простой ответ. Который когда-либо легче читать и понимать.

Ответ 6

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

Ответ 7

Нет никакой разницы в объемах памяти или производительности. Подлинный ИЛ - это всего лишь выбор команд ветвления/прыжка, поэтому на самом деле не имеет значения, вернутся ли они в начало цикла или в оператор "else".

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

Ответ 8

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

Ответ 9

Оригинал 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

Ответ 10

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

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

Если вам нравится версия "before" больше, чем "after", используйте ее.

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

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

Некоторые рефакторинги, выполненные ReSharper, приводятся даже в ситуациях, когда ReSharper предлагает противоположный рефакторинг.

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

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