Так что я программирую уже несколько лет, а недавно начал больше использовать ReSharper. Одна вещь, которую ReSharper всегда предлагает мне, - это «инвертировать» if, чтобы уменьшить вложенность ».
Допустим, у меня есть этот код:
foreach (someObject in someObjectList)
{
if(someObject != null)
{
someOtherObject = someObject.SomeProperty;
}
}
И ReSharper предложит мне сделать это:
foreach (someObject in someObjectList)
{
if(someObject == null) continue;
someOtherObject = someObject.SomeProperty;
}
Кажется, что ReSharper ВСЕГДА предложит мне инвертировать IF, независимо от того, сколько вложений происходит. Эта проблема в том, что я люблю вложенность, по крайней мере, в НЕКОТОРЫХ ситуациях. Мне кажется, легче читать и выяснять, что происходит в определенных местах. Это не всегда так, но иногда я чувствую себя более комфортно.
У меня вопрос: кроме личных предпочтений или читабельности, есть ли причина уменьшить вложенность? Есть ли разница в производительности или что-то еще, о чем я могу не знать?
источник
Ответы:
Это зависит. В вашем примере наличие неинвертированного
if
оператора не является проблемой, потому что телоif
очень короткое. Однако вы обычно хотите инвертировать утверждения, когда тела растут.Мы всего лишь люди, и одновременно держать в голове множество вещей сложно.
Когда тело оператора большое, обращение оператора не только уменьшает вложенность, но и говорит разработчику, что они могут забыть обо всем, что находится выше этого оператора, и сосредоточиться только на остальном. Вы, скорее всего, будете использовать инвертированные операторы, чтобы как можно скорее избавиться от неправильных значений. Как только вы узнаете, что они вышли из игры, единственное, что осталось, - это значения, которые действительно могут быть обработаны.
источник
break
, чтоcontinue
и множествоreturn
не структурировано.break
/continue
/return
имеют реальное преимущество передelse
блоком. При раннем выходе есть 2 блока : блок выхода и блок пребывания там; сelse
есть 3 блока : if, else и все, что будет после (которое используется независимо от выбранной ветви). Я предпочитаю иметь меньше блоков.Не избегайте негативов. Люди сосут, разбирая их, даже когда процессор их любит.
Однако соблюдайте идиомы. Мы, люди, являемся существами привычки, поэтому мы предпочитаем хорошо проложенные пути прямым путям, полным сорняков.
foo != null
К сожалению, стал идиомой. Я едва замечаю отдельные части больше. Я смотрю на это и просто думаю: «О, сейчас уже безопасноfoo
».Если вы хотите отменить это (о, когда-нибудь, пожалуйста), вам нужен действительно сильный аргумент. Вам нужно что-то, что позволит моим глазам легко скользить и понимать это в одно мгновение.
Однако то, что вы нам дали, было так:
Что даже не является структурно одинаковым!
Это не делает то, что ожидал мой ленивый мозг. Я думал, что смогу добавить независимый код, но не могу. Это заставляет меня думать о вещах, о которых я не хочу думать сейчас. Ты сломал мою идиому, но не дал мне лучшего. Все потому, что ты хотел защитить меня от единственного отрицателя, который сжег это отвратительное маленькое я в моей душе.
Извините, может быть, ReSharper и вправду предлагает это в 90% случаев, но при нулевых проверках я думаю, что вы нашли угловой случай, в котором его лучше игнорировать. Инструменты просто не способны научить вас, что такое читаемый код. Спроси человека. Сделайте рецензию. Но не следуйте слепо за инструментом.
источник
if (foo != null){ foo.something() }
позволяет мне добавлять код без заботы, если онfoo
был нулевым. Это не Даже если мне не нужно делать это сейчас, я не чувствую мотивации гадать на будущее, говоря: «Ну, они могут просто изменить его, если им это нужно». Фэ. Программное обеспечение мягкое, только если его легко изменить.Это старый аргумент о том, хуже ли разрыв, чем гнездование.
Люди, кажется, принимают ранний возврат для проверки параметров, но его не одобряет большая часть метода.
Лично я избегаю продолжения, разрыва, перехода и т. Д., Даже если это означает больше вложенности. Но в большинстве случаев вы можете избежать обоих.
Очевидно, что вложение делает сложным для понимания, когда у вас много слоев
Хуже всего, когда у вас есть оба, хотя
источник
foreach (subObject in someObjectList.where(i=>i != null))
не знаю, почему я никогда не думал о чем-то подобном.Проблема в
continue
том, что если вы добавите больше строк в код, логика усложнится и может содержать ошибки. напримерВы хотите вызвать
doSomeOtherFunction()
для всех someObject или только если не ноль? С оригинальным кодом у вас есть возможность, и она понятнее. Сcontinue
одним из них можно забыть «о, да, там мы пропустили нули», и у вас даже не будет возможности вызвать его для всех объектов someObject, если вы не поместите этот вызов в начало метода, который может оказаться невозможным или правильным по другим причинам.Да, много вложений уродливо и, возможно, сбивает с толку, но в этом случае я бы использовал традиционный стиль.
Бонусный вопрос: почему в этом списке для начала есть нули? Может быть, лучше вообще никогда не добавлять ноль, и тогда логика обработки будет намного проще.
источник
return
потому что они образуют «чистый разрыв», но IMObreak
иcontinue
приводят к путанице, и в большинстве случаев их лучше избегать.Идея заключается в скорейшем возвращении. Ранний
return
цикл становится раннимcontinue
.Много хороших ответов на этот вопрос: должен ли я вернуться из функции раньше или использовать оператор if?
Обратите внимание, что хотя вопрос закрыт как основанный на мнении, более 430 голосов проголосовали за досрочное возвращение, ~ 50 голосов «зависит» и 8 - против досрочного. Таким образом, существует исключительно сильный консенсус (либо это, либо я плохо считаю, что вполне вероятно).
Лично, если тело цикла будет подвергаться раннему продолжению и достаточно долго, чтобы оно имело значение (3-4 строки), я стремлюсь извлечь тело цикла в функцию, где я использую ранний возврат вместо раннего продолжения.
источник
Этот метод полезен для сертификации предварительных условий, когда основная часть вашего метода возникает при выполнении этих условий.
Мы часто инвертируем
ifs
на моей работе и используем призрак еще. Раньше довольно часто, просматривая пиар, я мог указать, как мой коллега мог убрать три уровня вложенности! Это случается реже, так как мы разворачиваем наши ifs.Вот игрушечный пример того, что можно выкатить
Код подобный этому, где есть ОДИН интересный случай (где остальные просто возвращаются или небольшие блоки операторов) являются общими. Тривиально переписать вышесказанное как
Здесь наш значимый код, состоящий из 10 строк, не имеет тройного отступа в функции. Если у вас есть первый стиль, то у вас возникает соблазн реорганизовать длинный блок в метод или допустить отступ. Вот тут-то и начинается экономия. В одном месте это тривиальная экономия, но во многих методах во многих классах вы избавляете от необходимости генерировать дополнительную функцию, чтобы сделать код чище, и у вас не так много отвратительного многоуровневого отступ .
В ответ на пример кода OP я согласен с тем, что это чрезмерный коллапс. Тем более что в 1TB, стиль, который я использую, инверсия приводит к увеличению размера кода.
источник
Предложения Resharpers часто полезны, но всегда должны оцениваться критически. Он ищет некоторые заранее определенные шаблоны кода и предлагает преобразования, но он не может учитывать все факторы, которые делают код более или менее читабельным для человека.
При прочих равных условиях предпочтительнее меньше вложенности, поэтому ReSharper предложит преобразование, уменьшающее вложенность. Но все остальные вещи не равны: в этом случае преобразование требует введения a
continue
, а это означает, что на самом деле сложный код будет сложнее следовать.В некоторых случаях обмен уровня вложенности на a действительно
continue
может быть улучшением, но это зависит от семантики рассматриваемого кода, которая выходит за рамки понимания механических правил ReSharpers.В вашем случае предложение ReSharper ухудшит код. Так что просто игнорируйте это. Или лучше: не используйте предложенное преобразование, но немного подумайте, как можно улучшить код. Обратите внимание, что вы можете назначать одну и ту же переменную несколько раз, но только последнее назначение будет иметь силу, так как предыдущее будет просто перезаписано. Это делает вид , как ошибка. Предложение ReSharpers не исправляет ошибку, но делает ее немного более очевидной, что происходит некоторая сомнительная логика.
источник
Я думаю, что более важным моментом здесь является то, что цель цикла диктует наилучший способ организации потока внутри цикла. Если вы отфильтровываете некоторые элементы перед тем, как перебрать оставшиеся, то
continue
имеет смысл отключать рано. Однако, если вы выполняете различные виды обработки в цикле, возможно, имеет смысл сделать этоif
действительно очевидным, например:Обратите внимание, что в Java Streams или других функциональных идиомах вы можете отделить фильтрацию от зацикливания, используя:
Между прочим, это одна из вещей, которые мне нравятся в обращенном к Perl if (
unless
), применяемом к отдельным операторам, который допускает следующий вид кода, который я нахожу очень простым для чтения:источник