Я видел, что они оба используются во многих фрагментах кода С#, и я хотел бы знать, когда использовать i++
или ++i
(i
как числовую переменную типа int
, float
, double
и т.д.). Кто знает это?
В чем разница между я ++ и ++ i?
Ответ 1
Как ни странно, похоже, что два других ответа не говорят об этом, и это определенно стоит сказать:
i++
означает "скажите мне значение i
, затем увеличивайте"
++i
означает "приращение i
, затем скажите мне значение"
Они являются пред-инкрементами, операторами после инкремента. В обоих случаях переменная увеличивается, но если вы должны принять значение обоих выражений в точно таких же случаях, результат будет отличаться.
Ответ 2
Типичный ответ на этот вопрос, к сожалению, уже опубликованный здесь, состоит в том, что один выполняет приращение "до" оставшихся операций, а другой - "после" оставшихся операций. Хотя эта идея интуитивно понятна, это утверждение совершенно неверно. Последовательность событий во времени чрезвычайно четко определена в С#, и совершенно не имеет значения, что префиксная (++var) и постфиксная (var++) версии ++ делают вещи в другом порядке по отношению к другим операции.
Неудивительно, что вы увидите много неправильных ответов на этот вопрос. Огромное количество книг "научи себя С#" также ошибаются. Кроме того, способ, которым С# делает это, отличается от того, как это делает C. Многие люди считают, что С# и C - это один и тот же язык; они не. На мой взгляд, конструкция операторов увеличения и уменьшения в С# позволяет избежать ошибок проектирования этих операторов в C.
Есть два вопроса, на которые нужно ответить, чтобы определить, что именно происходит с префиксом и постфиксом ++ в С#. Первый вопрос: каков результат? и второй вопрос: когда происходит побочный эффект приращения?
Не ясно, каков ответ на любой вопрос, но на самом деле все довольно просто, когда вы его видите. Позвольте мне объяснить вам, что именно x++ и ++x делают для переменной x.
Для префиксной формы (++x):
- x вычисляется для получения переменной
- Значение переменной копируется во временную папку
- Временное значение увеличивается, чтобы создать новое значение (не перезаписывая временное!)
- Новое значение сохраняется в переменной
- Результатом операции является новое значение (то есть увеличенное значение временного значения)
Для формы постфикса (x++):
- x вычисляется для получения переменной
- Значение переменной копируется во временную папку
- Временное значение увеличивается, чтобы создать новое значение (не перезаписывая временное!)
- Новое значение сохраняется в переменной
- Результатом операции является значение временного
Некоторые вещи, на которые стоит обратить внимание:
Во-первых, порядок событий во времени одинаков в обоих случаях. Опять же, это абсолютно не тот случай, когда порядок событий во времени меняется между префиксом и постфиксом. Совершенно неверно утверждать, что оценка происходит до других оценок или после других оценок. Оценки выполняются в абсолютно одинаковом порядке в обоих случаях, как можно видеть на шагах с 1 по 4, которые идентичны. только разница - это последний шаг - является ли результат значением временного или нового, увеличенного значения.
Вы можете легко продемонстрировать это с помощью простого консольного приложения на С#:
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
, основано на временном, а не фактическом значении, сохраненном в переменной в момент выхода из любой из этих операций.Напомним, что в порядке операций выше, первые два шага копируют текущее значение переменной во временное. Это то, что используется для расчета возвращаемого значения; в случае версии с префиксом это временное значение увеличивается, тогда как в случае версии с суффиксом это значение напрямую/не увеличивается. Сама переменная не читается снова после первоначального хранения во временную.
Проще говоря, постфиксная версия возвращает значение, которое было прочитано из переменной (то есть значение временного), в то время как префиксная версия возвращает значение, которое было записано обратно в переменную (то есть увеличенное значение временного). Ни один из них не возвращает значение переменной.
Это важно понимать, поскольку сама переменная может быть изменчивой и изменяться в другом потоке, что означает, что возвращаемое значение этих операций может отличаться от текущего значения, хранящегося в переменной.
Люди на удивление часто путаются в отношении приоритета, ассоциативности и порядка выполнения побочных эффектов, я подозреваю, что в основном потому, что это так запутанно в C. С# был тщательно спроектирован, чтобы быть менее запутанным во всех этих отношениях. Для некоторого дополнительного анализа этих проблем, включая мою дальнейшую демонстрацию ложности идеи о том, что префиксные и постфиксные операции "перемещают вещи во времени", смотрите:
http://blogs.msdn.com/b/ericlippert/archive/2009/08/10/precedence-vs-order-redux.aspx
что привело к такому вопросу:
int [] arr = {0}; int value = arr [arr [0] ++]; Значение = 1?
Вас также могут заинтересовать мои предыдущие статьи на эту тему:
http://blogs.msdn.com/b/ericlippert/archive/2008/05/23/precedence-vs-associativity-vs-order.aspx
и
http://blogs.msdn.com/b/ericlippert/archive/2007/08/14/c-and-the-pit-of-despair.aspx
и интересный случай, когда C затрудняет рассуждения о правильности:
http://blogs.msdn.com/b/ericlippert/archive/2005/04/28/bad-recursion-revisited.aspx
Кроме того, мы сталкиваемся с похожими тонкими проблемами при рассмотрении других операций, имеющих побочные эффекты, таких как простые цепочки назначений:
И вот интересный пост о том, почему операторы инкремента приводят к значениям в С#, а не в переменных:
Ответ 3
Если у вас есть:
int i = 10;
int x = ++i;
то x
будет 11
.
Но если у вас есть:
int i = 10;
int x = i++;
то x
будет 10
.
Обратите внимание, как указывает Эрик, приращение происходит одновременно в обоих случаях, но какое значение дает в результате, которое отличается (спасибо Eric!).
Как правило, мне нравится использовать ++i
, если нет веской причины. Например, при написании цикла я хотел бы использовать:
for (int i = 0; i < 10; ++i) {
}
Или, если мне просто нужно увеличивать переменную, мне нравится использовать:
++x;
Обычно одно или другое не имеет большого значения и сводится к стилю кодирования, но если вы используете операторы внутри других назначений (например, в моих оригинальных примерах), важно знать о потенциальных побочных эффектах.
Ответ 4
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.
Отвечает ли это на ваш вопрос?
Ответ 5
То, как работает оператор, заключается в том, что он получает приращение в одно и то же время, но если оно есть перед переменной, выражение будет оцениваться с помощью переменной/уменьшенной переменной:
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 в использовании pre-increment/decment (++ x), если это необходимо. Действительно, единственный раз, когда я использую post-increment/decment, находится в циклах или циклах такого типа. Эти петли одинаковы:
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);
Etc и т.д.
Ответ 6
Только для записи на С++, если вы можете использовать либо (то есть), вам не нужно упорядочивать операции (вы просто хотите увеличить или уменьшить и использовать ее позже), префиксный оператор более эффективен, так как не нужно создавать временную копию объекта. К сожалению, большинство людей используют posfix (var ++) вместо префикса (++ var), просто потому, что это то, что мы узнали изначально. (Об этом меня спрашивали в интервью). Не уверен, что это правда в С#, но я предполагаю, что это будет.