我看到它们都在大量的c#代码中使用,我想知道什么时候使用i++或++ I (I是一个数字变量,如int, float, double等)。有人知道吗?


如果你有:

int i = 10;
int x = ++i;

那么x等于11。

但如果你有:

int i = 10;
int x = i++;

那么x等于10。

注意,正如Eric指出的那样,在这两种情况下,增量同时发生,但不同的是作为结果给出的值(谢谢Eric!)

通常,我喜欢使用++ I,除非有很好的理由不这样做。例如,在编写循环时,我喜欢使用:

for (int i = 0; i < 10; ++i) {
}

或者,如果我只需要增加一个变量,我喜欢使用:

++x;

通常情况下,一种方式或另一种方式并没有太大的意义,而是归结于编码风格,但如果您在其他赋值中使用运算符(就像我最初的示例一样),那么了解潜在的副作用是很重要的。


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.

这是否回答了你的问题?


奇怪的是,看起来其他两个答案并没有说清楚,这绝对值得一说:


i++表示“告诉我I的值,然后递增”

++i表示“增加i,然后告诉我值”


它们是前增量运算符,后增量运算符。在这两种情况下,变量都是递增的,但是如果在完全相同的情况下取两个表达式的值,结果将会不同。


操作符的工作方式是它同时被加1,但如果它在变量之前,表达式将用加1 /减1的变量求值:

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循环或类似的循环中。这些循环是相同的:

while (x < 5)  //evaluates conditional statement
{
    //some code
    ++x;       //increments x
}

or

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);

等等,等等……


The typical answer to this question, unfortunately posted here already, is that one does the increment "before" remaining operations and the other does the increment "after" remaining operations. Though that intuitively gets the idea across, that statement is on the face of it completely wrong. The sequence of events in time is extremely well-defined in C#, and it is emphatically not the case that the prefix (++var) and postfix (var++) versions of ++ do things in a different order with respect to other operations.

对于这个问题,你会看到很多错误的答案,这并不奇怪。很多“自学c#”的书也搞错了。此外,c#的方式与C是不同的。许多人认为c#和C是同一种语言;事实并非如此。在我看来,c#中自增和自减操作符的设计避免了C中这些操作符的设计缺陷。

要确定在c#中前缀++和后缀++的操作究竟是什么,必须回答两个问题。第一个问题是结果如何?第二个问题是,增量的副作用什么时候会发生?

这两个问题的答案都不明显,但一旦你看到它,它实际上是相当简单的。我来详细解释一下x++和++x对于变量x的作用。

对于前缀形式(++x):

对X求值以生成变量 变量的值被复制到一个临时位置 临时值被递增以产生一个新值(不是覆盖临时值!) 新值存储在变量中 操作的结果是新值(即临时值的增量值)

对于后缀形式(x++):

对X求值以生成变量 变量的值被复制到一个临时位置 临时值被递增以产生一个新值(不是覆盖临时值!) 新值存储在变量中 操作的结果是临时的值

需要注意的一些事情:

First, the order of events in time is exactly the same in both cases. Again, it is absolutely not the case that the order of events in time changes between prefix and postfix. It is entirely false to say that the evaluation happens before other evaluations or after other evaluations. The evaluations happen in exactly the same order in both cases as you can see by steps 1 through 4 being identical. The only difference is the last step - whether the result is the value of the temporary, or the new, incremented value.

你可以用一个简单的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:{Application.currentValue} Passed-in:{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()之后,但正如您从结果中看到的,它发生在'Current:2'结果所指示的调用之前。

在这种情况下,首先将currentValue的值存储在一个临时对象中。接下来,该值的增量版本被存储回currentValue中,但不涉及仍然存储原始值的临时值。最后,该临时对象被传递给TestMethod()。如果增量发生在对TestMethod()的调用之后,那么它会写出相同的、不加1的值两次,但事实并非如此。

It's important to note that the value returned from both the currentValue++ and ++currentValue operations are based on the temporary and not the actual value stored in the variable at the time either operation exits. Recall in the order of operations above, the first two steps copy the then-current value of the variable into the temporary. That is what's used to calculate the return value; in the case of the prefix version, it's that temporary value incremented while in the case of the suffix version, it's that value directly/non-incremented. The variable itself is not read again after the initial storage into the temporary. Put more simply, the postfix version returns the value that was read from the variable (i.e. the value of the temporary) while the prefix version returns the value that was written back to the variable (i.e. the incremented value of the temporary). Neither return the variable's value. This is important to understand because the variable itself could be volatile and have changed on another thread which means the return value of those operations could differ from the current value stored in the variable.

令人惊讶的是,人们对优先级、结合性和副作用执行的顺序感到困惑是很常见的,我怀疑这主要是因为它在C中是如此令人困惑。c#经过精心设计,在所有这些方面都不那么令人困惑。有关这些问题的其他分析,包括我进一步证明前缀和后缀操作“及时移动东西”的想法的错误,请参阅:

https://ericlippert.com/2009/08/10/precedence-vs-order-redux/

这就引出了这个SO问题:

int [] arr = {0};Int值= arr[arr[0]++];Value = 1?

你可能也会对我之前关于这个主题的文章感兴趣:

https://ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order/

and

https://ericlippert.com/2007/08/14/c-and-the-pit-of-despair/

还有一个有趣的例子,C语言使得判断正确性变得很困难:

https://learn.microsoft.com/archive/blogs/ericlippert/bad-recursion-revisited

同样,在考虑其他有副作用的操作时,我们也会遇到类似的微妙问题,比如链式简单赋值:

https://learn.microsoft.com/archive/blogs/ericlippert/chaining-simple-assignments-is-not-so-simple

这里有一篇有趣的文章,讲了为什么自增操作符在c#中是值而不是变量:

为什么我不能在c类语言中做++i++ ?


只是为了记录,在c++中,如果你可以使用任何一种(即),你不关心操作的顺序(你只想增加或减少,然后再使用它),前缀操作符更有效,因为它不需要创建对象的临时副本。不幸的是,大多数人使用posfix (v++)而不是prefix (++var),因为这是我们最初学到的。(我在一次采访中被问到这个问题)。不确定这在c#中是否正确,但我假设它是正确的。


我想我会用代码来回答这个问题。想象一下int的以下方法:

// The following are equivalent:
//     ++i;
//     PlusPlusInt(ref i);
//
// The argument "value" is passed as a reference,
// meaning we're not incrementing a copy.
static int PlusPlusInt(ref int value)
{
    // Increment the value.
    value = value + 1;

    // Return the incremented value.
    return value;
}

// The following are equivalent:
//     i++;
//     IntPlusPlus(ref i);
//
// The argument "value" is passed as a reference,
// meaning we're not incrementing a copy.
static int IntPlusPlus(ref int value)
{
    // Keep the original value around before incrementing it.
    int temp = value;

    // Increment the value.
    value = value + 1;

    // Return what the value WAS.
    return temp;
}

假设你知道ref是如何工作的,这应该很好地解决了这个问题。在我看来,用英语解释它要笨拙得多。