简体   繁体   中英

C# if/else first statement working, the rest are not

I know this is probably very basic, and I googled it and have gone through the search, but I'm having an issue which a very basic if/else scenario. The first statement produces results, which tells me that I've messed up the other statements somehow. It's driving me a little crazy. FYI, this is my first experience with a form window.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Wk2_Part1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {  
            int season;
            int job;
            season = Convert.ToInt32(textBox1.Text);
            job = Convert.ToInt32(textBox2.Text);
            if (season == 1)
                if (job == 1)
                    label3.Text = "There is a 20% discount on the exterior job";
            else
                if (season == 2)
                    if (job == 1)
                        label3.Text = "There is a 20% discount on the exterior job";
            else
                if (season == 3)
                    if (job == 2)
                        label3.Text = "There is a 30% discount on the interior job";
            else
                 label3.Text = "No discount, regular prices apply";
        }
    }
 }

If I were to align your code block with how the compiler sees it, this is how it would look. You can see in the code below that since there are no explicit start and ends brackets for your if blocks that the else blocks are coupled with the closest previous if . As Peter said, the white space in C# does not matter like it does in python.

    private void button1_Click(object sender, EventArgs e)
    {  
        int season;
        int job;
        season = Convert.ToInt32(textBox1.Text);
        job = Convert.ToInt32(textBox2.Text);
        if (season == 1)
            if (job == 1)
                label3.Text = "There is a 20% discount on the exterior job";
            else
                if (season == 2)
                    if (job == 1)
                        label3.Text = "There is a 20% discount on the exterior job";
                    else
                        if (season == 3)
                            if (job == 2)
                                label3.Text = "There is a 30% discount on the interior job";
                            else
                                label3.Text = "No discount, regular prices apply";
    }

Now we could add some curly braces here to fix the issue.

    private void button1_Click(object sender, EventArgs e)
    {  
        int season;
        int job;
        season = Convert.ToInt32(textBox1.Text);
        job = Convert.ToInt32(textBox2.Text);
        if (season == 1) 
        {
            if (job == 1)
            {
                label3.Text = "There is a 20% discount on the exterior job";
            }
        }
        else
        {
            if (season == 2)
            {
                if (job == 1)
                {
                    label3.Text = "There is a 20% discount on the exterior job";
                }
            }
        }
        else
        {
            if (season == 3)
            {
                if (job == 2)
                {
                    label3.Text = "There is a 30% discount on the interior job";
                }
            }
        }
        else
        {
             label3.Text = "No discount, regular prices apply";
        }
    }

The problem with code like this is 2 fold. 1) it is not very readable, and 2) it is not very testable. As Peter also pointed out you can easily reduce the complexity of this code by combining some of the if statements as below.

if ((season == 1 || season == 2) && job == 1)
{
    label3.Text = "There is a 20% discount on the exterior job";
}
else if (season == 3 && job == 2)
{
    label3.Text = "There is a 30% discount on the interior job";
}
else
{
    label3.Text = "No discount, regular prices apply";
}

While this makes the code much easier to understand and reduces the duplication of the string messages, I would not stop here. In order for this code to be testable we need to remove the dependency it has on the fact that there is a button click, and presumably a form component involved ( label3 ). To do that we can move this block of code to a method that returns a string, rather than sets a string.

private void button1_Click(object sender, EventArgs e)
{  
    int season = Convert.ToInt32(textBox1.Text);
    int job = Convert.ToInt32(textBox2.Text);

    label3.Text = GetDiscount(season, job);
}

private String GetDiscount(int season, int job)
{
    if ((season == 1 || season == 2) && job == 1)
    {
        return "There is a 20% discount on the exterior job";
    }

    if (season == 3 && job == 2)
    {
        return "There is a 30% discount on the interior job";
    }

    return "No discount, regular prices apply";
}

In this manner we have decoupled the code from the form that is involved in inputing and displaying the data. We have also reduced the complexity even more by eliminating the need for the else statements. By returning the string from the method we exit the block of code as there is no need to continue performing the if checks.

C# is not Python. Lining an else up with if doesn't make that else go with that if . You need braces ( { and } ) to surround a block if you don't want the if in that block to get connected with the else after that block. You also need to make sure you always set the Text property even when the inner condition of an if block doesn't pass.

I would also modify your logic a bit so you don't have to repeat code:

if ((season == 1 || season == 2) && job == 1)
    label3.Text = "There is a 20% discount on the exterior job";
else if (season == 3 && job == 2)
    label3.Text = "There is a 30% discount on the interior job";
else
    label3.Text = "No discount, regular prices apply";

Of course, in the above I didn't put braces, even though I said you needed them in your original code. In fact, I personally prefer to always use braces, but the above is more consistent with your original formatting.

(In fact, if you look at any of my other posted answers with code, I doubt you'll find even a single example of an un-braced block of code following a flow-control statement, even when that block is just a single statement. If you find any, it will be like above where I'm intentionally just trying to match the original question's code.)

The difference between the two is that the braces designate a block of C# statements, which are treated as a single statement for the purpose of flow-control statements like if , else , for , while , etc. To fix your original code with braces would have required putting the inner if (job == 1) part inside the braces, and the else outside of them. That way, that inner if would be included in the entire block defined by the braces and not eligible to be matched up with the subsequent else (because branching/looping statements can't span code blocks defined by braces).

In the above, there is no inner if . There's just the single statement that is executed when the if condition is true . So the subsequent else , matching the nearest eligible if statement, works correctly.

Again: braces are only required when you have some block of statements that has to be made separate from the flow-control statements around it. It is fine to have just a bare statement if there's only one, and it's not an if that's going to get incorrectly matched with a following else without braces.

But, they may be used in other cases, even when not strictly necessary. And many people, myself included, feel it's better to use them after every flow-control statement to define the block you want controlled by that statement, even if not necessary.

The reason to do this is specifically to prevent the problem you ran into. By being explicit about which blocks of code go with which flow-control, you avoid such problems. Note that this isn't just something for novice programmers. Even experienced programmers can accidentally create bugs in the same way you did. It typically happens for a different reason: the code was originally fine with a single brace-less statement, but at a later date someone had to modify the code to add some logic. When they do this, they fail to notice the structure of the code and break the flow-control logic that was intended. Sometimes it's just that a statement winds up outside the flow control, other times it's more like your scenario where an else winds up matched with the wrong if . Either way, it's bad news and easy enough to avoid simply by always including the braces.

This is how C# read your code.

if (season == 1)
    if (job == 1)
        label3.Text = "There is a 20% discount on the exterior job";
    else if (season == 2)
        if (job == 1)
            label3.Text = "There is a 20% discount on the exterior job";
        else if (season == 3)
            if (job == 2)
                label3.Text = "There is a 30% discount on the interior job";
            else
                label3.Text = "No discount, regular prices apply";

C# does not consume your indents for its logic. So better use brackets.

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