Інвертуйте оператор "якщо", щоб зменшити розміщення


272

Наприклад, коли я запустив ReSharper на свій код, наприклад:

    if (some condition)
    {
        Some code...            
    }

ReSharper видав мені вищезазначене попередження (Інвертувати заявку "якщо" для зменшення введення) та запропонував таке виправлення:

   if (!some condition) return;
   Some code...

Я хотів би зрозуміти, чому це краще. Я завжди вважав, що використовувати "повернення" в середині методу проблематично, дещо як "goto".


1
Я вважаю, що перевірка виключень і повернення на початку добре, але я змінив би умову, щоб ви перевіряли виняток безпосередньо, а не щось (тобто якщо повернення (деякий умова)).
bruceatk

36
Ні, це нічого не зробить для виконання.
Сет Карнегі

3
Мені б сподобалося кинути ArgumentException, якби мій метод передавав погані дані.
asawyer

1
@asawyer Так, тут є ціла побічна дискусія щодо функцій, які занадто прощають нісенітницькі введення - на відміну від використання помилки у твердженні. Написання твердого коду відкрило мені очі на це. У такому випадку це було б щось подібне ASSERT( exampleParam > 0 ).
Грег Хендершот

4
Твердження стосуються внутрішнього стану, а не параметрів. Для початку слід перевірити параметри, стверджуючи, що ваш внутрішній стан правильний, а потім виконати операцію. У збірці випусків ви можете або випустити твердження, або зіставити їх з відключенням компонента.
Саймон Ріхтер

Відповіді:


296

Повернення в середині методу не обов’язково погано. Можливо, буде краще повернутися негайно, якщо це зробить наміри коду яснішими. Наприклад:

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
     return result;
};

У цьому випадку, якщо _isDeadце правда, ми можемо негайно вийти з методу. Можливо, краще структурувати це таким чином:

double getPayAmount() {
    if (_isDead)      return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired)   return retiredAmount();

    return normalPayAmount();
};   

Я вибрав цей код із каталогу рефакторингу . Цей специфічний рефакторинг називається: Замініть вкладене умовне на захисні пропозиції.


13
Це справді приємний приклад! Код, що відновлюється, читає більше як випадок справи.
Otherside

16
Напевно, лише справа смаку: я пропоную змінити 2-е та 3-е "якщо" на "інше, якщо", щоб ще більше збільшити читабельність. Якщо хтось оглядає заяву "return", все одно буде зрозуміло, що наступний випадок перевіряється лише у тому випадку, коли попередній не вдався, тобто важливий порядок перевірок.
foraidt

2
У прикладі цей простий, я згоден, другий спосіб краще, але тільки тому, що його так очевидно, що відбувається.
Ендрю Буллок

Тепер, як ми можемо сприймати цей симпатичний код jop написаний і не дозволяє візуальній студії не розбивати його і ставити віддачу в окремі рядки? Мені це дійсно дратує те, що він переформатує код таким чином. Це робить дуже читабельний код UGLY.
Марк Т

@Mark T У візуальній студії є налаштування, щоб запобігти порушенню цього коду.
Аарон Сміт

333

Це не тільки естетично , але й знижує максимальний рівень гніздування всередині методу. Це, як правило, розцінюється як плюс, оскільки це полегшує розуміння методів (і справді, багато інструментів статичного аналізу дають змогу визначити це як один із показників якості коду).

З іншого боку, ваш метод також має декілька точок виходу, те, що інша група людей вважає, що це немає.

Особисто я погоджуюся з ReSharper та першою групою (мовою, яка має винятки, мені здається дурним обговорювати "кілька точок виходу"; майже все можна кинути, тому у всіх методах є численні потенційні точки виходу).

Щодо продуктивності : обидві версії повинні бути еквівалентними (якщо не на рівні IL, то, безумовно, після закінчення тремтіння з кодом) на всіх мовах. Теоретично це залежить від компілятора, але практично будь-який широко використовуваний сьогодні компілятор здатний обробляти набагато більш досконалі випадки оптимізації коду, ніж цей.


41
одиничні точки виходу? Кому це потрібно?
sq33G

3
@ sq33G: Питання щодо SESE (і відповіді, звичайно) є фантастичним. Дякуємо за посилання!
Джон

Я постійно чую відповіді, що є деякі люди, які виступають за єдині точки виходу, але я ніколи не бачив, щоб хтось насправді виступав за це, особливо на таких мовах, як C #.
Томас Боніні

1
@AndreasBonini: Відсутність доказів не є доказом відсутності. :-)
Jon

Так, звичайно, мені просто дивно, що всі відчувають потребу сказати, що деяким людям подобається інший підхід, якщо ці люди не відчувають потреби говорити це самі =)
Томас Боніні

102

Це трохи релігійний аргумент, але я погоджуюся з ReSharper, що вам слід віддати перевагу менш вкладеним. Я вважаю, що це переважає негативи наявності кількох шляхів повернення від функції.

Основна причина меншого розміщення - поліпшення читабельності коду та ремонтопридатності . Пам'ятайте, що багатьом іншим розробникам у майбутньому потрібно буде прочитати ваш код, а код із меншим відступом, як правило, набагато простіше читати.

Передумови - чудовий приклад того, коли нормально повернутися рано на початку функції. Чому на читабельність решти функції має впливати наявність перевірки передумов?

Щодо негативів щодо повернення декількох разів з методу - налагоджувачі зараз досить потужні, і дізнатися, де і коли повертається певна функція, дуже просто.

Наявність декількох повернень у функції не вплине на роботу програміста з технічного обслуговування.

Погана читабельність коду буде.


2
Кілька повернень у функції впливають на програміста технічного обслуговування. Під час налагодження функції, шукаючи повернене значення повернення, обслуговуючий персонал повинен поставити точки перерви у всіх місцях, які можуть повернутися.
EvilTeach

10
Я поставив би точку розриву на вступній дузі. Якщо ви перейдете через цю функцію, ви не тільки зможете побачити перевірки, які виконуються в порядку, але і буде повністю зрозуміло, яка перевірка функції приземлилася для цього запуску.
Джон Дунаган

3
Погодьтеся з Іваном тут ... Я не бачу проблеми в тому, щоб просто переступити через всю функцію.
Nailer

5
@Nailer Дуже пізно, я особливо погоджуюся, тому що якщо функція занадто велика, щоб переступити, вона все одно повинна бути розділена на кілька функцій!
Айдіакапі

1
Погодьтеся і з Іваном. Можливо, це стара школа думала поставити крапку внизу, але якщо вам цікаво, яка функція повертається, то ви перейдете через цю функцію, щоб побачити, чому вона все одно повертається. Якщо ви просто хочете побачити, що він повертається, тоді поставте його на останню дужку.
user441521

70

Як зазначали інші, не повинно бути хіт на виставу, але є й інші міркування. Окрім цих поважних проблем, це також може відкрити вас до приїздів за певних обставин. Припустимо, ви doubleнатомість мали справу з :

public void myfunction(double exampleParam){
    if(exampleParam > 0){
        //Body will *not* be executed if Double.IsNan(exampleParam)
    }
}

Порівнюйте це з наче рівнозначною інверсією:

public void myfunction(double exampleParam){
    if(exampleParam <= 0)
        return;
    //Body *will* be executed if Double.IsNan(exampleParam)
}

Тож за певних обставин те, що видається правильно перевернутим, ifможе бути і не.


4
Слід також зазначити, що швидке виправлення повторного перетворення інвертує exampleParam> 0 у exampleParam <0, а не exampleParam <= 0, що мене вигнало.
нік

1
І саме тому ця перевірка повинна бути передньою та повернутою, якщо врахувати, що дев вважає, що няня повинна призвести до викупу.
user441521

51

Ідея повернення лише в кінці функції повернулася з тих днів, коли мови мали підтримку винятків. Це дало змогу програмам покладатися на те, що зможуть поставити код очищення в кінці методу, а потім бути впевненим, що він буде викликаний, а інший програміст не приховуватиме повернення методу, який спричинив пропуск коду очищення . Пропущений код очищення може призвести до витоку пам'яті або ресурсів.

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

У всякому разі, я кажу, що я думаю, що багато людей цитують керівництво "лише повернення в кінці методу", не розуміючи, чому це колись було добре робити, і що зменшення вкладеності для покращення читабельності, ймовірно, є кращою метою.


6
Ви щойно з’ясували, чому винятки - UglyAndEvil [tm] ... ;-) Винятки - це гото в химерних та дорогих маскуваннях.
EricSchaefer

16
@Eric Ви, мабуть, зазнали надзвичайно поганого коду. Це досить очевидно, коли вони використовуються неправильно, і вони, як правило, дозволяють писати високоякісний код.
Роберт Полсон,

Це відповідь, яку я справді шукав! Тут є чудові відповіді, але більшість з них зосереджена на прикладах, а не на фактичній причині, чому ця рекомендація народилася.
Густаво Морі

Це найкраща відповідь, оскільки вона пояснює, чому весь цей аргумент виник в першу чергу.
Буттер Буткус

If you've got deep nesting, maybe your function is trying to do too many things.=> це не правильний наслідок попередньої фрази. Тому що перед тим, як сказати, що ви можете змінити поведінку рефактора A з кодом C на поведінку A з кодом D. код D чистіший, наданий, але "занадто багато речей" стосується поведінки, яка НЕ ​​змінилася. Тому ви не маєте сенсу з цим висновком.
v.oddou

30

Я хотів би додати, що є ім'я для тих, хто перевернуто, якщо є - Застереження. Я використовую його, коли можу.

Я ненавиджу код для читання там, де на початку два екрани коду та більше нікого. Просто оберніть, якщо і повернутися. Таким чином ніхто не витрачатиме час на прокрутку.

http://c2.com/cgi/wiki?GuardClause


2
Саме так. Це скоріше рефакторинг. Це допомагає читати код простіше, як ви згадали.
rpattabi

"повернення" недостатньо і жахливе для запобіжного застереження. Я б зробив: якщо (ex <= 0) кинути WrongParamValueEx ("[MethodName] Неправильний параметр введення значення 1 {0} ... і вам потрібно буде
зафіксувати

3
@John. Ви праві, якщо це насправді помилка. Але часто це не так. І замість того, щоб перевіряти щось у кожному місці, де метод називається метод, просто перевіряє його і повертає нічого не роблячи.
Пьотр Перак

3
Мені б не хотілося читати метод, який складається з двох екранів коду, періоду, ifs чи ні. lol
jpmc26

1
Особисто я віддаю перевагу ранньому поверненню саме з цієї причини (також: уникати гніздування). Однак це не пов'язане з початковим запитанням щодо продуктивності і, ймовірно, має бути коментарем.
Патрік М

22

Це не тільки впливає на естетику, але й запобігає введенню коду.

Насправді він може функціонувати як передумова для того, щоб ваші дані також були дійсними.


18

Це, звичайно, суб'єктивно, але я думаю, що це сильно покращується у двох аспектах:

  • Зараз відразу очевидно, що вашій функції нічого не залишається робити, якщо вона conditionвиконується.
  • Це дозволяє знизити рівень гніздування. Вкладення шкодить читабельності більше, ніж ви думаєте.

15

Кілька точок повернення були проблемою в C (і меншою мірою C ++), оскільки вони змусили вас дублювати код очищення перед кожною з точок повернення. Зі збиранням сміття, try| finallyконструювати та usingблокувати, насправді немає причин, чому ви повинні їх боятися.

Зрештою, це зводиться до того, що вам та вашим колегам легше читати.


Це не єдина причина. Існує наукова причина, що стосується псевдокоду, який не має нічого спільного з практичними міркуваннями, такими як прибирання. Ця акамедична причина пов'язана з дотриманням основних форм імперативних конструкцій. Так само, як ви не ставите петлеві витяжки в його середину. Таким чином, інваріанти можуть бути жорстко виявлені і поведінка може бути доведена. Або припинення можна довести.
v.oddou

1
новини: насправді я знайшов дуже практичну причину, чому ранні перерви та повернення погані, і це прямий наслідок сказаного, статичного аналізу. ось ви йдете, посібник з використання компілятора intel C ++: d3f8ykwhia686p.cloudfront.net/1live/intel/… . Ключова фраза:a loop that is not vectorizable due to a second data-dependent exit
v.oddou

12

В залежності від продуктивності, між двома підходами помітної різниці не буде.

Але кодування - це більше, ніж продуктивність. Чіткість та ремонтопридатність також дуже важливі. І у таких випадках, коли це не впливає на продуктивність, це єдине, що має значення.

Існують змагальні школи думок щодо того, який підхід є кращим.

Один погляд - це той, про який згадували інші: другий підхід знижує рівень гніздування, що підвищує чіткість коду. Це природно в імперативному стилі: коли тобі нічого не залишається, ти можеш також повернутися рано.

Інший погляд, з точки зору більш функціонального стилю, полягає в тому, що метод повинен мати лише одну точку виходу. Все у функціональній мові - це вираз. Тож якщо у висловлюваннях завжди повинні бути інші пункти. Інакше вираз if не завжди має значення. Тож у функціональному стилі перший підхід є більш природним.


11

Застереження або попередні умови (як ви, мабуть, бачите) перевірте, чи виконується певна умова, а потім порушує потік програми. Вони чудово підходять для місць, де вас насправді цікавить лише один результат ifзаяви. Тому замість того, щоб сказати:

if (something) {
    // a lot of indented code
}

Ви скасовуєте умову та перериваєтесь, якщо ця обернена умова виконується

if (!something) return false; // or another value to show your other code the function did not execute

// all the code from before, save a lot of tabs

return ніде не так брудно, як goto . Це дозволяє передавати значення, щоб показати решту коду, що функція не могла запускатися.

Ви побачите найкращі приклади того, як це можна застосувати в вкладених умовах:

if (something) {
    do-something();
    if (something-else) {
        do-another-thing();
    } else {
        do-something-else();
    }
}

vs:

if (!something) return;
do-something();

if (!something-else) return do-something-else();
do-another-thing();

Ви знайдете мало людей, які стверджують, що перший - це чистіше, але, звичайно, це абсолютно суб'єктивно. Деякі програмісти люблять знати, в яких умовах щось працює за допомогою відступів, тоді як я б швидше тримав потоки методу лінійними.

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


6
Я думаю, я один із небагатьох тоді. Мені легше читати першу версію. Вкладені ifs роблять дерево рішень більш очевидним. З іншого боку, якщо є кілька передумов, я згоден, краще поставити їх усі на вершині функції.
сторони

@Otherside: повністю згоден. повернення, написані серійно, змушує ваш мозок потребувати серіалізації можливих шляхів. Коли дерево дерева if може бути відображене безпосередньо в деякій перехідній логіці, наприклад, його можна скласти до "lisp". але серійне повернення вимагає компілятора набагато важче зробити. Суть тут, очевидно, не має нічого спільного з цим гіпотетичним сценарієм, це стосується аналізу коду, шансів на оптимізацію, корекційних доказів, доказів припинення та виявлення інваріантів.
v.oddou

1
Ні в якому разі не буде легше читати код із більшою кількістю гнізд. Чим більше блоку, як щось, тим простіше читати з першого погляду. Тут немає жодного способу, коли я легше читати, і це лише 2 гнізда, коли більшість подібних кодів у реальному світі заглиблюється, і з кожним гніздом потрібно більше мозку для наслідування.
user441521

1
Якби гніздування було вдвічі глибшим, скільки часу знадобиться би ти, щоб відповісти на питання: "Коли ти робиш щось інше?" Я думаю, що вам важко буде відповісти на це без пера та паперу. Але ти міг би відповісти на це досить легко, якби це все було застережено.
Девід Сторфер

9

Тут зроблено кілька хороших моментів, однак кілька точок повернення також можуть бути нечитатими , якщо метод дуже тривалий. При цьому, якщо ви збираєтесь використовувати декілька точок повернення, просто переконайтесь, що ваш метод короткий, інакше бонус за читання декількох точок повернення може бути втрачений.


8

Продуктивність складається з двох частин. У вас є продуктивність, коли програмне забезпечення виробляється, але ви також хочете мати продуктивність під час розробки та налагодження. Останнє, що хоче розробник, - це «чекати» чогось тривіального. Зрештою, компіляція цього з увімкненою оптимізацією призведе до подібного коду. Тож добре знати ці маленькі хитрощі, які окупаються в обох сценаріях.

Справа у питанні зрозуміла, ReSharper правильний. Замість того, щоб вкладати ifзаяви і створювати нову область в коді, ви встановлюєте чітке правило на початку свого методу. Це збільшує читабельність, легше буде дотримуватися, і це зменшує кількість правил, які вам доведеться просіяти, щоб знайти, куди вони хочуть піти.


7

Особисто я віддаю перевагу лише 1 точці виходу. Це легко здійснити, якщо ви триматимете свої методи короткими і точними, і це забезпечує передбачуваний зразок для наступної людини, яка працює над вашим кодом.

напр.

 bool PerformDefaultOperation()
 {
      bool succeeded = false;

      DataStructure defaultParameters;
      if ((defaultParameters = this.GetApplicationDefaults()) != null)
      {
           succeeded = this.DoSomething(defaultParameters);
      }

      return succeeded;
 }

Це також дуже корисно, якщо ви просто хочете перевірити значення певних локальних змінних у функції перед тим, як вона закінчується. Все, що вам потрібно зробити, - це поставити точку зламу на остаточному поверненні, і ви гарантовано вдаритесь по ньому (якщо не буде викинуто виняток).


4
bool PerformDefaultOperation () {DataStructure defaultParameters = this.GetApplicationDefaults (); Возврат (замовчувані = NULL && this.DoSomething (замовчувані);} Там, встановили її для вас :).
tchen

1
Цей приклад є дуже базовим і пропускає точку цього шаблону. Проведіть близько 4 інших перевірок всередині цієї функції, а потім скажіть нам, що це читабельніше, адже це не так.
користувач441521

5

Багато вагомих причин того, як виглядає код . А як щодо результатів ?

Давайте подивимось на якийсь код C # та його скомпільовану форму IL:


using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length == 0) return;
        if ((args.Length+2)/3 == 5) return;
        Console.WriteLine("hey!!!");
    }
}

Цей простий фрагмент можна скласти. Ви можете відкрити згенерований .exe файл за допомогою ildasm і перевірити, який результат. Я не буду публікувати всю роботу з асемблером, але опишу результати.

Створений код IL виконує наступні дії:

  1. Якщо перша умова помилкова, переходить до коду, де є друга.
  2. Якщо це правда, переходить до останньої інструкції. (Примітка: остання інструкція - це повернення).
  3. У другій умові те саме відбувається після підрахунку результату. Порівняйте та: потрапили до Console.WriteLine, якщо помилкові, або до кінця, якщо це правда.
  4. Роздрукуйте повідомлення та поверніться.

Тож здається, що код підскочить до кінця. Що робити, якщо ми робимо нормальний, якщо з вкладеним кодом?

using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length != 0 && (args.Length+2)/3 != 5) 
        {
            Console.WriteLine("hey!!!");
        }
    }
}

Результати досить схожі в інструкціях щодо ІЛ. Різниця полягає в тому, що раніше були стрибки за умовою: якщо помилка переходить до наступного фрагмента коду, якщо правда - перейти до кінця. І тепер код IL протікає краще і має 3 стрибки (компілятор трохи оптимізував це): 1. Перший стрибок: коли довжина дорівнює 0 до частини, де код знову стрибає (Третій стрибок) до кінця. 2. Другий: посеред другої умови уникати однієї інструкції. 3. Третє: якщо друга умова помилкова, перескочіть до кінця.

У будь-якому випадку лічильник програм завжди буде стрибати.


5
Це гарна інформація - але особисто мені було б менше байдуже, якщо ІЛ «протікає краще», тому що люди, які збираються керувати кодом, не побачать ІЛ
JohnIdol

1
Ви дійсно не можете передбачити, як функціонуватиме оптимізація при наступному оновлення компілятора C # порівняно з тим, що ви бачите сьогодні. Особисто я б не витрачав жодного часу, намагаючись підправити код C #, щоб створити інший результат в ІЛ, якщо тільки тестування не покаже, що у вас є проблема продуктивності SEVERE в якомусь щільному циклі десь і у вас не вистачає інших ідей . Навіть тоді я б подумав важче про інші варіанти. У цьому ж сенсі я сумніваюся, що ви маєте будь-яке уявлення про те, якими оптимізаціями займається компілятор JIT з IL, що отримує його для кожної платформи ...
Крейг,

5

Теоретично інвертування if може призвести до кращої продуктивності, якщо збільшить швидкість прогнозування галузей. На практиці я думаю, що дуже важко точно знати, як поводитиметься передбачення галузей, особливо після складання, тому я б не робив цього в своїй щоденній розробці, за винятком випадків, коли пишу асемблерний код.

Більше про галузеве прогнозування тут .


4

Це просто суперечливо. Немає «згоди між програмістами» щодо питання якнайшвидшого повернення. Наскільки я знаю, це завжди суб'єктивно.

Можна зробити аргумент ефективності, оскільки краще мати умови, записані так, що вони найчастіше відповідають дійсності; можна також стверджувати, що це зрозуміліше. З іншого боку, це створює вкладені тести.

Я не думаю, що ви отримаєте остаточну відповідь на це питання.


Я не розумію вашого аргументу ефективності. Умови булеві, тому незалежно від результату, завжди є два результати ... Я не бачу, чому відміна заяви (чи ні) змінила б результат. (Якщо ви не скажете, що додавання "НЕ" до умови додає вимірювану кількість обробки ...
Oli

2
Оптимізація працює так: Якщо ви переконаєтесь, що найпоширеніший випадок - це в блоці if, а не в блоці else, то процесор, як правило, вже має висловлювання з блоку if, завантаженого в його конвеєр. Якщо умова повернеться помилково, процесору потрібно спорожнити конвеєр і ...
Otherside

Мені також подобається робити те, що говорять інші, просто для читабельності, я ненавиджу негативні випадки в блоці "тоді", а не "інше". Це має сенс робити, навіть не знаючи і не зважаючи на те, що робить процесор
Ендрю Буллок

3

Тут вже є багато проникливих відповідей, але все-таки я хотів би спрямувати на дещо іншу ситуацію: замість того, щоб передумову поставити над функцією, подумайте про покрокову ініціалізацію, де ви доведеться перевірити кожен крок, щоб досягти успіху, а потім продовжити наступний. У цьому випадку ви не можете перевірити все вгорі.

Я вважав свій код дійсно нечитабельним, коли писав хост-програму ASIO за допомогою ASIOSDK Steinberg, коли дотримувався парадигми введення. Це пішло як вісім рівнів глибоко, і я не можу побачити вади дизайну там, про що згадував Ендрю Буллок вище. Звичайно, я міг би запакувати якийсь внутрішній код до іншої функції, а потім вклав там інші рівні, щоб зробити його більш читабельним, але це здається мені досить випадковим.

Замінивши гніздування на захисні пропозиції, я навіть виявив помилкове уявлення про частину коду очищення, який повинен був відбутися набагато раніше в межах функції, а не в кінці. З вкладеними гілками я ніколи цього не бачив, можна навіть сказати, що вони призвели до моєї помилки.

Тож це може бути інша ситуація, коли перевернуті ifs можуть сприяти більш чіткому коду.


3

Уникнення кількох точок виходу може призвести до підвищення продуктивності. Я не впевнений, що стосується C #, але в C ++ оптимізація іменованого зворотного значення (Copy Elision, ISO C ++ '03 12.8 / 15) залежить від наявності однієї точки виходу. Ця оптимізація дозволяє уникнути побудови копії для повернення значення (у вашому конкретному прикладі це не має значення). Це може призвести до значного збільшення продуктивності в тісних петлях, оскільки ви зберігаєте конструктор і деструктор щоразу, коли функція викликається.

Але в 99% випадків збереження додаткових викликів конструктора та деструктора не варто ifвводити втрату читабельності вкладених блоків (як вказували інші).


2
там. мені знадобилося три довгих читання десятків відповідей у ​​3 запитаннях із запитанням того ж самого (2 з яких заморожені), щоб нарешті знайти когось, хто згадує про NRVO. да ... дякую.
v.oddou

2

Це питання думки.

Моїм нормальним підходом було б уникати однорядних ifs, а повертається в середині методу.

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


2

На мою думку, раннє повернення добре, якщо ви просто повертаєте недійсні (або якийсь непотрібний код повернення, який ви ніколи не перевірите), і це може покращити читабельність, оскільки ви уникаєте гніздування, і в той же час ви робите чітке уявлення про свою функцію.

Якщо ви насправді повертаєте returnValue - вкладення, як правило, є кращим способом, тому що ви повертаєте returnValue просто в одному місці (в кінці - duh), і це може зробити ваш код більш рентабельним у цілій безлічі випадків.


1

Я не впевнений, але думаю, що R # намагається уникати далеких стрибків. Коли у вас IF-ELSE, компілятор робить щось подібне:

Умова false -> далеко перейти до false_condition_label

true_condition_label: інструкція1 ... інструкція_n

false_condition_label: інструкція1 ... інструкція_n

кінцевий блок

Якщо умова справжня, немає стрибка та кешу L1 розгортання, але перехід до false_condition_label може бути дуже далеко, і процесор повинен розгорнути власний кеш. Синхронізувати кеш - це дорого. R # намагається замінити далекі стрибки на короткі стрибки, і в цьому випадку більша ймовірність того, що всі інструкції вже є в кеші.


0

Я думаю, це залежить від того, що ви віддаєте перевагу, як зазначалося, немає загальної згоди afaik. Щоб зменшити роздратування, ви можете зменшити подібне попередження до "Підказки"


0

Моя ідея полягає в тому, що повернення "посеред функції" не повинно бути таким "суб'єктивним". Причина досить проста: візьміть цей код:

    функція do_something (дані) {

      if (! is_valid_data (дані)) 
            повернути помилкове;


       do_something_that_take_an_hour (дані);

       istance = новий object_with_very_painful_constructor (дані);

          якщо (istance недійсне) {
               повідомлення про помилку( );
                повернення;

          }
       connect_to_database ();
       get_some_other_data ();
       повернення;
    }

Можливо, перше "повернення" - це НЕ ТАК інтуїтивно, але це справді економить. Занадто багато "ідей" щодо чистих кодів, яким просто потрібно більше практики, щоб втратити свої "суб'єктивні" погані ідеї.


За винятком мови у вас немає цих проблем.
user441521

0

У цього виду кодування є кілька переваг, але для мене велика перемога, якщо ви зможете швидко повернутися, ви можете покращити швидкість свого застосування. IE Я знаю, що через передумову X я можу швидко повернутися з помилкою. Це спочатку позбавляється від випадків помилок і зменшує складність вашого коду. У багатьох випадках, оскільки трубопровід процесора тепер може бути чистішим, він може зупинити аварії трубопроводів або комутатори. По-друге, якщо ви знаходитесь у циклі, швидке вирвання або повернення може заощадити багато процесора. Деякі програмісти використовують циклічні інваріанти для швидкого виходу з цього виду, але в цьому випадку ви можете порушити конвеєр процесора і навіть створити проблему пошуку пам’яті і означати, що процесор повинен завантажуватися з зовнішнього кешу. Але в основному я думаю, ви повинні робити те, що задумали, тобто кінець циклу або функції не створюють складний шлях коду просто для реалізації деякого абстрактного поняття правильного коду. Якщо єдиним інструментом у вас є молоток, то все схоже на цвях.


Читаючи відповідь graffic, це, скоріше, звучить так, як вкладений код був оптимізований компілятором таким чином, що виконаний код швидше, ніж використання декількох повернень. Однак, навіть якщо декілька повернень були швидшими, ця оптимізація, ймовірно, не пришвидшить вашої програми настільки сильно ... :)
пристойно

1
Я не згоден - Перший закон про те, щоб алгоритм був правильним. Але ми всі Інженери, що полягає у вирішенні правильної проблеми з ресурсами, які ми маємо та робимо у доступному бюджеті. Занадто повільна програма не підходить за призначенням.
Девід Аллан Фінч
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.