简体   繁体   中英

C# increment ToString

I add an unexpected behaviour from C#/WPF

    private void ButtonUp_Click(object sender, RoutedEventArgs e)
    {
        int quant;
        if( int.TryParse(Qnt.Text, out quant))
        {
            string s = ((quant++).ToString());
            Qnt.Text = s;
        }
    }

So, if I get quant as 1, quant will be incremented to 2. But the s string will be 1. Is this a question of precedence?

EDIT:

I re-wrote this as:

            quant++;
            Qnt.Text = quant.ToString();

and now this works as I expected.

The problem is that you're using a post-increment instead of a pre-increment... but why would you want to write this convoluted code? Just separate out the side-effect (incrementing) and the ToString call:

if (int.TryParse(Qnt.Text, out quant))
{
    quant++;
    Qnt.Text = quant.ToString();
}

Or even forego the actual increment given that you're not going to read the value again:

if (int.TryParse(Qnt.Text, out quant))
{
    Qnt.Text = (quant + 1).ToString();
}

Where possible, avoid using compound assignment in the middle of other expressions. It generally leads to pain.

Additionally, it feels like all this parsing and formatting is hiding the real model, which is that there should be an int property somewhere, which might be reflected in the UI. For example:

private void ButtonUp_Click(object sender, RoutedEventArgs e)
{
    // This is an int property
    Quantity++;
    // Now reflect the change in the UI. Ideally, do this through binding
    // instead.
    Qnt.Text = Quantity.ToString();
}

You are using the post -increment operator. This evalutates to the original value, and then increments. To do what you want in a one-liner you can use the pre -increment operator instead.

(++quant).ToString();

But even better would be to avoid all such pitfalls and do it like this:

quant++;
string s = quant.ToString();

With the first version you have to think about the order in which things happen. In the second version no thought is required. Always value code clarity more highly than conciseness.

It's easy to believe that the one-line version is somehow faster, but that's not true. It might have been true back in the day in 1970s C systems, but even then that I doubt.

Now I'll do something that shouldn't be done... I'll try to simplify what Eric Lippert wrote here What is the difference between i++ and ++i? I hope I'm not writing anything too much wrong :-)

Now... What does the pre-increment and post-increment operators do? Simplifying and ignoring all the copy that are done in-between (and remembering that they aren't atomic operators in multi-threaded environments):

both of them are expressions (like i + 1 ) that return a result (like i + 1 ) but that have a side-effect (unlike i + 1 ). The side-effect is that they increment the variable i . The big question is "in which order everything happens?" The answer is quite simple:

  • pre increment ++i : increments i and returns the new value of i
  • post increment i++ : increments i and returns the old value of i

Now... The important part is that the increments i always happens first. Then a value (the old or the new) is returned.

Let's make an example (the example of Lippert is quite complex. I'll make a different, more simple example, that isn't as much complete but that is enough to check if the order I said before is right or not) (technically I'll make two examples)

Example 1:

unchecked
{
    int i = Int32.MaxValue;
    Console.WriteLine("Hello! I'm trying to do my work here {0}", i++);
    Console.WriteLine("Work done {1}", i);
}

Example 2:

checked
{
    int i = Int32.MaxValue;
    Console.WriteLine("Hello! I'm trying to do my work here {0}", i++);
    Console.WriteLine("Work done {1}", i);
}

checked means that if there is an overflow an exception ( OverflowException ) will be thrown. unchecked means that the same operation won't throw an exception. Int32.MaxValue + 1 surely will overflow. With checked there will be an exception, with unchecked i will become -1.

Let's try running the first code piece. Result:

Hello! I'm trying to do my work here 2147483647
Work done -1

Ok... The i was incremented but the Console.WriteLine received the old value ( Int32.MaxValue == 2147483647 ). From this example we can't determine the order of the post-increment and of the calling of Console.WriteLine .

Let's try running the second code piece. Result:

System.OverflowException: Arithmetic operation resulted in an overflow.

Ok... It's quite clear that first the post-increment was executed, caused an exception, and then clearly the Console.WriteLine wasn't executed (because the program ended).

So we know that the order I said is the right one.

Now. What should you learn from this example? The same thing I learned many years ago. Pre and post increments in C and C# are good for obfuscated code contests. They aren't good for many other things (but note that C++ is different!). From that lesson I learned that there are exactly two places where you can use post-increment freely, and there are exactly zero places where you can use pre-increment freely.

"Safe" post-increment

for (int i = 0; i < x; i++)

and

i++; // Written alone. Nothing else on the same line but a comment if necessary.

"Safe" pre-increment

(nothing)

In this case, first quant.ToString() will be called and then quant will be incremented.

If you write ((++quant).ToString()) the first step will be incrementing quant and then quant.ToString() will be called.

string s = ((quant++).ToString());

can be distributed as

use quant for toString() method call before incrementing, and then

execute assignment operator, and then

increment `quant'

try with ++quant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM