Навіщо оголошувати змінні близько до того, де вони використовуються?


10

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

Наприклад, ця політика пропонує мені це зробити:

foreach (var item in veryLongList) {
  int whereShouldIBeDeclared = item.Id;
  //...
}

Але, безумовно, це означає, що intна кожній ітерації виникають накладні витрати на створення нового . Чи не було б краще використовувати:

int whereShouldIBeDeclared;
foreach (var item in veryLongList) {
  whereShouldIBeDeclared = item.Id;
  //...
}

Будь ласка, хтось може пояснити?


3
Це була б досить проклята бідна мова, яка стосувалася цих двох випадків по-різному.
Пол Томблін

5
Ви починаєте з помилкового передумови. Будь ласка , дивіться мій відповідь на це питання: stackoverflow.com/questions/6919655 / ...
CesarGon

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

4
Якщо два приклади коду мають суттєву смислову різницю, то вони роблять різні речі. Вам слід використовувати той, що робить те, що ви хочете робити. Правило про декларування змінних застосовується лише до випадків, коли це не має смислової різниці.
Девід Шварц

4
Розглянемо протилежний кінець шкали - все є глобальною змінною. Безумовно, "декларований близький до використання" кращий кінець цього спектру?
JBRWilkinson

Відповіді:


27

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

Справжня причина цього правила також є причиною того, що ви не бачите, чому це правило. Люди писали функції, що складали сотні, навіть тисячі рядків, і вони писали їх у текстових редакторах (думаю, Блокнот) без такої підтримки, яку надала Visual Studio. У цьому середовищі оголошення змінної сотні рядків від місця її використання означало, що людина, яка читає

if (flag) limit += factor;

не було багато підказок про те, що таке прапор, межа та коефіцієнт. Для сприяння цьому було прийнято називати такі конвенції, як угорська нотація, і такі правила, як декларування речей, близьких до місця їх використання. Звичайно, в наші дні все стосується рефакторингу, а функції, як правило, менше сторінки, тому важко провести велику відстань між тим, де декларуються речі, і де вони використовуються. Ви працюєте в діапазоні 0-20 і хитаєтесь, що, можливо, 7 нормально в цьому конкретному випадку, тоді як хлопець, який створив правило, ЛЮБИТЬ, щоб забрати 7 рядків і намагався розмовляти з кимось з 700. І далі Крім того, у Visual Studio ви можете навести курсор миші на що-небудь і побачити його тип, чи є це змінною члена тощо. Це означає, що необхідність бачити рядок, що оголошує її, зменшується.

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


Дякую за вашу відповідь. Але, безумовно, незалежно від типу даних, створюється новий екземпляр для кожної ітерації залежно від того, яким чином я це роблю? Просто у другому випадку ми щоразу не просимо нової посилання на пам'ять. Або я пропустив суть? А ви кажете, що оптимізатор C # автоматично покращить мій код, коли він все одно збиратиметься? Я цього не знав!
Джеймс

2
Накладні витрати на створення int є мінімальними. Якби ви будували щось складне, накладні кошти були б більшою справою.
Кейт Григорій

17
Справа не лише в тому, щоб мати можливість бачити її тип і таке. Це також питання життя. Якщо змінна "wibble" оголошена за 30 рядків до її першого використання, існує 30 рядків, в яких помилкове використання "wibble" може призвести до помилки. Якщо це оголошено безпосередньо перед його використанням, використання "wibble" у цих 30 попередніх рядках не призведе до помилок. Це натомість призведе до помилки компілятора.
Майк Шеррілл 'Відкликання котів'

У цьому випадку новий екземпляр створюється не кожен цикл. Єдина змінна верхнього рівня створюється і використовується для кожної ітерації (дивіться на ІЛ). Але це деталізація реалізації.
thecoop

"у Visual Studio ви можете навести курсор миші на що-небудь і побачити" і т. д. Існує також Перейдіть до визначення, для якого необхідний ярлик F12.
StuperUser

15

Визначення змінної всередині циклу робить її видимість локальною лише для цього циклу. Це має щонайменше 3 переваги для читача:

  1. Визначення змінної та будь-які пов'язані з цим коментарі легко знайти
  2. Читач знає, що ця змінна ніколи не використовується в іншому місці (залежність не очікувати)
  3. Коли код написаний чи відредагований, немає ймовірності, що ви могли б використати те саме ім’я змінної за межами циклу, щоб посилатися на цю змінну, інакше ви можете помилитися.

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


4

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


4

Хоча це допомагає в читанні, читабельність в цьому випадку не є першочерговою увагою, а сучасні ІДЕ не уникають потреби в цьому правилі.

Основна проблема - неініціалізовані змінні. Якщо ви оголосите змінну занадто далеко від її ініціалізації, вона відкриє вам всі можливі проблеми. Ви можете виявити, що ви випадково працюєте з тим, що траплялося раніше в оперативній пам’яті, або результатом обчислення, вищого за функцією, або ініціалізації манекена (як 0), яку хтось вводив, щоб уникнути скарга компілятора. Люди вставлять код між вашою декларацією та використанням, не усвідомлюючи ваших неявних передумов для цієї змінної. У гірших випадках таке використання буде просто працювати у ваших тестах, але не виходить з місця.

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


1

Це не "обов'язково". Це просто думка, я щось роблю. Наприклад, я люблю оголошувати всі зміни в перших рядках методу, так що я можу прокоментувати, що я з цим робитиму (звичайно, якщо вони не лічильники). Інші люди, як ви чули, люблять розміщувати їх якомога ближче до їх використання (як у другому прикладі, який ви написали). У будь-якому випадку, перший приклад, який ви подаєте, - це, безумовно, "помилка" (в тому сенсі, що це спричинить накладні витрати, як ви розумієте).

Ви просто повинні вибрати свій шлях і слідувати ним.


2
Це не просто думка, чи не так? Невже дослідження з програмного забезпечення не зафіксували взаємозв'язок між часом та кількістю помилок принаймні 1980-х?
Майк Шеррілл 'Відкликання котів'

1

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.