Лучший способ отформатировать оператор if с несколькими условиями

88

Если вы хотите, чтобы какой-то код выполнялся на основе двух или более условий, как лучше всего отформатировать этот оператор if?

первый пример: -

if(ConditionOne && ConditionTwo && ConditionThree)
{
   Code to execute
}

Второй пример: -

if(ConditionOne)
{
   if(ConditionTwo )
   {
     if(ConditionThree)
     {
       Code to execute
     }
   }
}

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

Гай Кодер
источник
Жалко, что никто на этой странице не упоминает подстроку «быстро» или «производительность». Я пришел сюда, чтобы узнать.
Президент Dreamspace

Ответы:

131

Я предпочитаю вариант А

bool a, b, c;

if( a && b && c )
{
   //This is neat & readable
}

Если у вас есть особенно длинные переменные / условия метода, вы можете просто разорвать их на строку

if( VeryLongConditionMethod(a) &&
    VeryLongConditionMethod(b) &&
    VeryLongConditionMethod(c))
{
   //This is still readable
}

Если они еще более сложные, то я бы подумал о том, чтобы делать методы условия отдельно вне оператора if.

bool aa = FirstVeryLongConditionMethod(a) && SecondVeryLongConditionMethod(a);
bool bb = FirstVeryLongConditionMethod(b) && SecondVeryLongConditionMethod(b);
bool cc = FirstVeryLongConditionMethod(c) && SecondVeryLongConditionMethod(c);

if( aa && bb && cc)
{
   //This is again neat & readable
   //although you probably need to sanity check your method names ;)
}

IMHO Единственная причина для варианта «B» - это если у вас есть отдельные elseфункции для запуска для каждого условия.

например

if( a )
{
    if( b )
    {
    }
    else
    {
        //Do Something Else B
    }
}
else
{
   //Do Something Else A
}
Эоин Кэмпбелл
источник
Мне это нравится. Хотя я не большой поклонник идеи метода, если только методы уже существуют и не возвращают логическое значение.
Thomas Owens
2
Разве во втором примере вы не оцениваете все без причины?
Odys
Я бы использовал "&&" перед условием. Трудно иметь условия с такой же длиной символа, как в примере.
Дарков
28

Другие ответы объясняют, почему обычно лучше всего первый вариант. Но если у вас несколько условий, подумайте о создании отдельной функции (или свойства), выполняющей проверку условий в варианте 1. Это значительно упрощает чтение кода, по крайней мере, при использовании хороших имен методов.

if(MyChecksAreOk()) { Code to execute }

...

private bool MyChecksAreOk()
{ 
    return ConditionOne && ConditionTwo && ConditionThree;
}

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

Торбьёрн
источник
2
Я обнаружил, что это наиболее эффективный и простой
способ
+1 я сначала приподнял бровь но это действительно лучший ответ имхо. наличие этого логического значения isOkToDoWhateverв качестве свойства имеет большой смысл.
grinch
1
Но это просто перемещает то же сложное условие в другое место, где оно также должно быть читаемым, поэтому мы вернулись к исходной точке с этим. Дело не только в ifудобочитаемости операторов, но и в удобочитаемости условий.
Роберт Коритник
@RobertKoritnik Я понимаю, о чем вы говорите, но не думаю, что мы вернулись к исходной точке, потому что мы снизили сложность, которую читатель должен учитывать за один раз. Она может посмотреть на условия, ИЛИ она может посмотреть на код, используя условия, в которых условия имеют (надеюсь) красивое имя. По крайней мере, мне часто легче это понять, но, опять же, иногда приятно собрать все детали в одном месте. Как всегда, зависит от обстоятельств.
Торбьёрн
Отличный смысл использовать хорошие имена методов и рефакторинг логики.
JB Lovell
10

Первый пример более «читаемый».

На самом деле, на мой взгляд, вы должны использовать только второй вариант, когда вам нужно добавить некоторую «логику else», но для простого Conditional используйте первый вариант. Если вас беспокоит длина условия, вы всегда можете использовать следующий синтаксис:

if(ConditionOneThatIsTooLongAndProbablyWillUseAlmostOneLine
                 && ConditionTwoThatIsLongAsWell
                 && ConditionThreeThatAlsoIsLong) { 
     //Code to execute 
}

Удачи!

Дэвид Сантамария
источник
9

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

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

Если два теста действительно являются двумя сторонами одной медали, например. если (x> 0) && (x <= 100), то поместите их в одну строку. Если другое условие концептуально гораздо более отдаленное, например. user.hasPermission (Admin ()), затем поместите его в отдельную строку

Например.

if user.hasPermission(Admin()) {
   if (x >= 0) && (x < 100) {
      // do something
   }
}
Interstar
источник
7
if (   ( single conditional expression A )
    && ( single conditional expression B )
    && ( single conditional expression C )
   )
{
   opAllABC();
}
else
{
   opNoneABC();
}

Форматирование нескольких условных выражений в выражении if-else следующим образом:

  1. обеспечивает улучшенную читаемость:
    a. все двоичные логические операции {&&, ||} в выражении, показанном первым
    b. оба условных операнда каждой бинарной операции очевидны, потому что они выровнены по вертикали
    c. Операции вложенных логических выражений становятся очевидными с помощью отступов, как и вложения операторов внутри предложения
  2. требует явных скобок (не полагаться на правила приоритета операторов)
    a. это позволяет избежать распространенных ошибок статического анализа
  3. позволяет упростить отладку файла
    . отключить отдельные одиночные условные тесты с помощью только a //
    b. установить точку
    останова непосредственно перед или после любого отдельного теста ceg ...
// disable any single conditional test with just a pre-pended '//'
// set a break point before any individual test
// syntax '(1 &&' and '(0 ||' usually never creates any real code
if (   1
    && ( single conditional expression A )
    && ( single conditional expression B )
    && (   0
        || ( single conditional expression C )
        || ( single conditional expression D )
       )
   )
{
   ... ;
}

else
{
   ... ;
}
Шон
источник
Это мой метод. Единственная проблема, с которой я столкнулся, это то, что мне еще предстоит найти средство для украшения кода, которое предлагало бы это в качестве опции
Speed8ump
4

Второй - классический пример антипаттерна стрелки, поэтому я бы его избегал ...

Если ваши условия слишком длинные, извлеките их в методы / свойства.

Омар Кухеджи
источник
3

Первый вариант проще, потому что, если вы читаете его слева направо, вы получите: «Если что-то И что-то еще И что-то еще ТО», что легко понять. Второй пример гласит: «Если что-то ТО, если что-то еще ТО, если что-то еще ТО», что неуклюже.

Также подумайте, хотите ли вы использовать несколько OR в своем предложении - как бы вы это сделали во втором стиле?

РБ.
источник
0

В Perl вы могли сделать это:

{
  ( VeryLongCondition_1 ) or last;
  ( VeryLongCondition_2 ) or last;
  ( VeryLongCondition_3 ) or last;
  ( VeryLongCondition_4 ) or last;
  ( VeryLongCondition_5 ) or last;
  ( VeryLongCondition_6 ) or last;

  # Guarded code goes here
}

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

Брэд Гилберт
источник
1
Вот и смотрит Перлиш - в строке «ЧТО делает?» смысл;) Но на самом деле он читается, как только вы к нему привыкнете.
Писквор покинул здание
-2

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

Если альтернативы нет, то, как предлагали другие, - разбейте ее на отдельные и сократите имена или сгруппируйте их, и, например, если все должно быть истинным, используйте что-то вроде «если нет ложного в массиве x, тогда запускайте».

Если ничего не получится, @Eoin Campbell высказал довольно хорошие идеи.

Типпер Лок
источник
Это не добавляет ничего нового к уже существующим ответам.
jerney
-4

Когда условие действительно сложное, я использую следующий стиль (пример из реальной жизни PHP):

if( $format_bool &&
    (
        ( isset( $column_info['native_type'] )
            && stripos( $column_info['native_type'], 'bool' ) !== false
        )
        || ( isset( $column_info['driver:decl_type'] )
            && stripos( $column_info['driver:decl_type'], 'bool' ) !== false
        )
        || ( isset( $column_info['pdo_type'] )
            && $column_info['pdo_type'] == PDO::PARAM_BOOL
        )
    )
)

Я считаю, что это лучше и удобнее, чем вложение нескольких уровней if(). И в некоторых случаях, подобных этому, вы просто не можете разбить сложное условие на части, потому что в противном случае вам пришлось бы if() {...}многократно повторять одни и те же операторы в блоке.

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

CodeDriller
источник