Я бачив їх обох використовуються в численних шматків коду С #, і я хотів би знати , коли використовувати i++
або ++i
( i
будучи змінним числом , як int
, float
, double
і т.д.). Хто це знає?
Я бачив їх обох використовуються в численних шматків коду С #, і я хотів би знати , коли використовувати i++
або ++i
( i
будучи змінним числом , як int
, float
, double
і т.д.). Хто це знає?
Відповіді:
Як не дивно, це виглядає так, що два інших відповіді не прописують це, і це, безумовно, варто сказати:
i++
означає "скажіть мені значення i
, а потім приріст"
++i
означає "приріст i
, то скажіть мені значення"
Вони є до-інкрементними, післякріпленими операторами. В обох випадках змінна збільшується , але якщо ви взяли значення обох виразів в абсолютно однакових випадках, результат буде відрізнятися.
Типова відповідь на це питання, на жаль, вже розміщене тут, полягає в тому, що один робить приріст "до", що залишився, а інший збільшує "після", що залишився. Незважаючи на те, що інтуїтивно зрозуміла ця ідея, це твердження на очах у неї абсолютно неправильне . TheПослідовність подій у часі дуже добре визначені в C #, і це рішуче НЕ той випадок, коли префікс (++ вару) і постфікси (вар ++) версія ++ робити речі в іншому порядку по відношенню до інших операцій.
Не дивно, що ви побачите багато неправильних відповідей на це питання. Дуже багато книг "навчати себе C #" теж неправильно розуміють. Також спосіб C # це відрізняється від того, як це робить C. Багато людей міркують так, ніби C # і C - одна і та ж мова; вони не. Конструкція операторів збільшення та зменшення в C #, на мій погляд, дозволяє уникнути вад дизайну цих операторів у C.
На це потрібно відповісти два питання, щоб визначити, що саме в C # є операцією префікса та постфікса ++. Перше питання - що це за результат?а друге питання - коли відбувається побічний ефект збільшення?
Не очевидно, що відповідь на будь-яке питання, але насправді це досить просто, як тільки ви його бачите. Дозвольте чітко прописати для вас, що x ++ та ++ x роблять для змінної x.
Для форми префікса (++ x):
Для форми постфікса (x ++):
Деякі речі, які слід помітити:
По-перше, порядок подій у часі точно однаковий в обох випадках . Знову ж таки, абсолютно не так, що порядок подій у часі змінюється між префіксом та постфіксом. Цілком помилково сказати, що оцінювання відбувається до інших оцінок або після інших оцінок. Оцінки відбуваються в абсолютно однаковому порядку в обох випадках, як ви бачите, що кроки 1 - 4 однакові. Лише відмінністю є останнім кроком - будь то результатом є значення тимчасового або нового, збільшується значення.
Ви можете легко продемонструвати це за допомогою простого додатка консолі C #:
public class Application
{
public static int currentValue = 0;
public static void Main()
{
Console.WriteLine("Test 1: ++x");
(++currentValue).TestMethod();
Console.WriteLine("\nTest 2: x++");
(currentValue++).TestMethod();
Console.WriteLine("\nTest 3: ++x");
(++currentValue).TestMethod();
Console.ReadKey();
}
}
public static class ExtensionMethods
{
public static void TestMethod(this int passedInValue)
{
Console.WriteLine("Current:{0} Passed-in:{1}",
Application.currentValue,
passedInValue);
}
}
Ось результати ...
Test 1: ++x
Current:1 Passed-in:1
Test 2: x++
Current:2 Passed-in:1
Test 3: ++x
Current:3 Passed-in:3
У першому тесті ви бачите, що currentValue
і те, і те, що було передано вTestMethod()
розширення, показують однакове значення, як і очікувалося.
Однак у другому випадку люди спробують сказати вам, що приріст currentValue
трапляється після дзвінка до TestMethod()
, але як ви бачите з результатів, це відбувається перед викликом, як зазначено в результаті "Поточний: 2".
У цьому випадку спочатку значення currentValue
зберігається у тимчасовому. Далі нарощена версія цього значення зберігається назад, currentValue
але не торкаючись тимчасової, яка все ще зберігає початкове значення. Нарешті, тимчасове передається в TestMethod()
. Якщо приріст трапився після виклику до TestMethod()
того, він би записував одне і те ж, непримножене значення вдвічі, але це не так.
Важливо зауважити, що значення, повернене як для операцій, так
currentValue++
і для++currentValue
операцій, засноване на тимчасовому, а не фактичному значенні, збереженому в змінній у момент, коли будь-яка операція закінчується.Нагадаємо, в порядку вищеописаних операцій перші два кроки копіюють поточне значення змінної у тимчасове. Саме це використовується для обчислення поверненого значення; у випадку з версією префікса це тимчасове значення збільшується, тоді як у випадку з суфіксною версією це значення безпосередньо / не збільшується. Сама змінна не читається знову після початкового зберігання у тимчасове.
Простіше кажучи, версія postfix повертає значення, яке було прочитане зі змінної (тобто значення тимчасового), тоді як версія префікса повертає значення, яке було записане назад, до змінної (тобто збільшення вартості тимчасового). Не повертайте значення змінної.
Це важливо зрозуміти, оскільки сама змінна може бути мінливою і змінитися на іншій потоці, що означає, що повернене значення цих операцій може відрізнятися від поточного значення, що зберігається в змінній.
На диво звичайно, що люди дуже заплутані у перевазі, асоціативності та порядку виконання побічних ефектів, я підозрюю, головним чином, оскільки це так заплутано в C. C # було ретельно розроблено, щоб бути менш заплутаним у всіх цих питаннях. Для додаткового аналізу цих питань, зокрема для подальшого демонстрування помилковості ідеї, що операції з префіксами та постфіксами "переміщують речі в часі", див:
https://ericlippert.com/2009/08/10/precedence-vs-order-redux/
що призвело до цього питання ТА:
int [] arr = {0}; int значення = arr [arr [0] ++]; Значення = 1?
Можливо, вам будуть цікаві і мої попередні статті на цю тему:
https://ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order/
і
https://ericlippert.com/2007/08/14/c-and-the-pit-of-despair/
і цікавий випадок, коли C важко міркувати про правильність:
https://docs.microsoft.com/archive/blogs/ericlippert/bad-recursion-revisited
Крім того, ми стикаємося з подібними тонкими проблемами, розглядаючи інші операції, які мають побічні ефекти, такі як прикуті прості призначення:
https://docs.microsoft.com/archive/blogs/ericlippert/chaining-simple-assignments-is-not-so-simple
Ось цікавий пост про те, чому оператори приросту призводять до значень у C #, а не до змінних :
i++
або ++i
використовуються в коді, речі , що відбуваються в тлі щойно; на задньому плані . Я пишу на C #, щоб піднятися на рівні абстракції вище того, що відбувається на цьому рівні, тому, якщо це дійсно має значення для вашого коду C #, ви, можливо, вже не знаєте мовою.
i++;
) for (int i = 0; i < x; i++)
... І я дуже дуже щасливий цьому! (і я ніколи не використовую оператор префікса). Якщо мені доведеться написати щось, що вимагатиме від старшого програміста 2 хвилини, щоб розшифрувати ... Ну ... Краще написати ще один рядок коду або ввести тимчасову змінну :-) Я думаю, що ваша "стаття" (я виграв не називаю це "відповідь") підтверджує мій вибір :-)
Якщо у вас є:
int i = 10;
int x = ++i;
тоді x
буде 11
.
Але якщо у вас є:
int i = 10;
int x = i++;
тоді x
буде 10
.
Зауважимо, як зазначає Ерік, приріст відбувається одночасно в обох випадках, але це значення, яке надається як результат, який відрізняється (дякую Еріку!).
Як правило, я люблю використовувати, ++i
якщо немає вагомих причин цього не робити. Наприклад, при написанні циклу я люблю використовувати:
for (int i = 0; i < 10; ++i) {
}
Або якщо мені просто потрібно збільшити змінну, я люблю використовувати:
++x;
Зазвичай той чи інший спосіб не має великого значення і зводиться до стилю кодування, але якщо ви використовуєте операторів всередині інших завдань (як, наприклад, у моїх оригінальних прикладах), важливо знати про можливі побічні ефекти.
i
для назви змінної, а не var
як ключове слово C #.
int i = 0;
Console.WriteLine(i++); // Prints 0. Then value of "i" becomes 1.
Console.WriteLine(--i); // Value of "i" becomes 0. Then prints 0.
Чи відповідає це на ваше запитання?
Оператор працює таким чином, що він збільшується одночасно, але якщо він знаходиться перед змінною, вираз буде оцінено за допомогою збільшення / зменшення змінної:
int x = 0; //x is 0
int y = ++x; //x is 1 and y is 1
Якщо це після змінної, поточний оператор виконуватиметься з початковою змінною, як би він ще не був збільшений / зменшений:
int x = 0; //x is 0
int y = x++; //'y = x' is evaluated with x=0, but x is still incremented. So, x is 1, but y is 0
Я погоджуюся з dcp у використанні попереднього збільшення / зменшення (++ x), якщо це не потрібно. Дійсно, єдиний раз, коли я використовую пост-інкремент / декремент, є в той час, як цикли або петлі такого типу. Ці петлі однакові:
while (x < 5) //evaluates conditional statement
{
//some code
++x; //increments x
}
або
while (x++ < 5) //evaluates conditional statement with x value before increment, and x is incremented
{
//some code
}
Ви також можете це робити під час індексації масивів і таких:
int i = 0;
int[] MyArray = new int[2];
MyArray[i++] = 1234; //sets array at index 0 to '1234' and i is incremented
MyArray[i] = 5678; //sets array at index 1 to '5678'
int temp = MyArray[--i]; //temp is 1234 (becasue of pre-decrement);
І т.д. тощо.
Тільки для запису, в C ++, якщо ви можете використовувати або (тобто) вам не байдуже впорядкування операцій (ви просто хочете збільшити або зменшити і використовувати його пізніше), оператор префікса є більш ефективним, оскільки він не робить повинні створити тимчасову копію об’єкта. На жаль, більшість людей використовує posfix (var ++) замість префікса (++ var), тільки тому, що це ми дізналися спочатку. (Мене про це запитали в інтерв'ю). Не впевнений, що це правда в C #, але я припускаю, що це було б.