-1

In my program, I have a method that has code that is used always when it is run. However, in the same method, I have if statements that branches off the code based on a state machine. Let's just (for simplicity) say that it's made of two values.

using System;
using System.Console;

int stateA = 1;
int stateB = 1;

void Main () {
     while (true) {
         Method();
     }
}

void Method () {
    Console.WriteLine("This will always be printed no matter what happens.");

    if (stateA == 1 && stateB == 1)
    {
         Console.WriteLine("State 1");
         stateB = 2;
         // goto endofifstatement;
    }
    elseif (stateA == 1 && stateB == 2)
    {
         Console.WriteLine("State 2");
         stateA = 2;
         // goto endofifstatement;
    }
    else {
         Console.WriteLine("Resetting States...")
         stateA = 1;
         stateB = 2;
         // goto endofifstatement;
    }
    // endofifstatement:

    Console.WriteLine("This will always also be printed no matter what happens.");
    Console.WriteLine("");
}

Now. The problem with this code right now, is that all three parts of the if...elseif...else block will run in every loop iteration.
Of course, there are several ways I could fix this:

  • Separate (encapsulate) the if...elseif....else statement into its own method and use return to break out.
  • Swap the contents and expressions for the if and elseif block, but that'd only work in this case. What happens if we have a few hundred cases?
  • Use goto, but nobody really uses that anymore. (Commented in the snippet above)
  • Use multiple switch...case or IF statements, nested inside each other.

Question:

Is it possible to escape out of a if...elseif...else statement like break does to a loop and return does for methods?

So for the example above, the output should be:

This will always be printed no matter what happens.
State 1
This will always also be printed no matter what happens.

This will always be printed no matter what happens.
State 2
This will always also be printed no matter what happens

This will always be printed no matter what happens.
Resetting States...
This will always also be printed no matter what happens

And repeat, as opposed to:

This will always be printed no matter what happens.
State 1
State 2
Resetting States...
This will always also be printed no matter what happens

This will always be printed no matter what happens.
State 1
State 2
Resetting States...
This will always also be printed no matter what happens

Aaand repeat. Plus, if we extend this to have more than two variables and possible values, it should be able to hold up, even at the expanse of CPU speed, since we're just essentially breaking out of a block.

Timothy
  • 364
  • 1
  • 12
  • 24
  • 1
    Maybe just throw in a `for` loop and a counter on top of `if` `else` ? – P. Pat Mar 14 '17 at 01:51
  • 2
    What do you mean all 3 branches run with the current code? I think you need to revisit that. – itsme86 Mar 14 '17 at 01:52
  • @itsme86 Yeah. I just realised. – Timothy Mar 14 '17 at 01:53
  • 1
    No, there’s nothing like that. You would use `goto` for the exact equivalent, like you said. – Ry- Mar 14 '17 at 01:54
  • 4
    Title question already answered: http://stackoverflow.com/a/2314089/103167 (Yes, it also works in C#) For your actual code, the `else` keyword is already causing exactly that behavior you mention in the comments. – Ben Voigt Mar 14 '17 at 01:58
  • To add to what itsme86 and Ben Voigt said above, the behavior assumed in the question is what you would get if you had used `if () ... if () ... if () ...` instead. – Junuxx Mar 14 '17 at 02:03
  • The code you posted already does what you want. So, what's your question? You got one half-way decent answer, in that it includes a suggestion to encapsulate `if` blocks as methods and use `return` statements to exit early, but that's not actually needed in the scenario you posit here. So though the answer is okay, it's an answer to a completely different question. What question do _you_ have? – Peter Duniho Mar 14 '17 at 06:53
  • @PeterDuniho I was just fixing it up for clarity to future visitors. That answer below is half-baked and it kinda goes against what I already asked; "What happens when there are more cases and variables?" and It only answers for that particular case. Oh, and it's kinda correct. Also, the answer I was looking for is a comment :/ – Timothy Mar 14 '17 at 06:58

2 Answers2

1

Yes, goto statement cause problems because you have to hunt for the line you want to execute next. In addition, it can cause unexpected behaviors in particularly complex code.

You have a number of options available to you. My preference would be to rewrite your logic so it is easier to read (Option 1), but I'll demonstrate the ones I see.

Option 1: Rewrite

void Method() {
    Console.WriteLine("This will always be printed no matter what happens.");
    if (stateA == 1) {
        if (stateB == 1) {
            Console.WriteLine("State 1");
            stateB = 2;
        }
        else {
            Console.WriteLine("State 2");
            stateA = 2;
        }
    }
    else {
        Console.WriteLine("Resetting States...")
        stateA = 1;
        stateB = 2;
    }

    Console.WriteLine("This will always also be printed no matter what happens.");
    Console.WriteLine("");
}

Option 2: Separate method

void UpdateState() {
    if (stateA == 1 && stateB == 1) {
        Console.WriteLine("State 1");
        stateB = 2;
        return;
    }
    elseif (stateA == 1 && stateB == 2) {
        Console.WriteLine("State 2");
        stateA = 2;
        return;
    }
    else {
        Console.WriteLine("Resetting States...")
        stateA = 1;
        stateB = 2;
        return;
    }
}
void Method() {
    Console.WriteLine("This will always be printed no matter what happens.");
    UpdateState();

    Console.WriteLine("This will always also be printed no matter what happens.");
    Console.WriteLine("");
}

Option 3: try/finally

void Method() {
    Console.WriteLine("This will always be printed no matter what happens.");
    try {
        if (stateA == 1 && stateB == 1) {
            Console.WriteLine("State 1");
            stateB = 2;
            return;
        }
        elseif (stateA == 1 && stateB == 2) {
            Console.WriteLine("State 2");
            stateA = 2;
            return;
        }
        else {
            Console.WriteLine("Resetting States...")
            stateA = 1;
            stateB = 2;
            return;
        }
    }
    finally {
        Console.WriteLine("This will always also be printed no matter what happens.");
        Console.WriteLine("");
    }
}

Option 4: Encapsulation/Polymorphism There are a number of design patterns that would apply here. In particular, Strategy and State come to mind.

Chad Greer
  • 56
  • 3
  • 1
    `UpdateState()` in Option 2: Separate method does not need `return;` – GSP Mar 14 '17 at 02:28
  • @GSP: and, for that matter, the original code does not need a `break;`. The problem with the answer above is that it addresses a non-problem with code that is more complicated than the original and yet does _exactly_ the same thing. – Peter Duniho Mar 14 '17 at 06:50
-1

From what I understand, you are trying to write a cleaner code when the elseif ladder grows very long. If that the case and NOT cpu performance then you have a few options:

State pattern

Have interface ASuitableNameState with method getNextState which return type of ASuitableNameState. Now you can have concrete implementations like class StateAOneBOne : ASuitableNameState and class StateAZeroBOne : ASuitableNameState.

In StateAOneBOne you override the method getNextState . There you return new StateAOneBTwo();

This way your if-else ladder vanishes. Your main can be rewritten as:

using System;
using System.Console;

ASuitableNameState currentState = new StateAOneBOne();

void Main () {
     while (true) {
         Method();
     }
}

void Method () {
    consolw.writeLine("A and B are");
    console.writeLine(currentState.getA + currentState.getB);
    currentState = currentState.getNextState();
}

The downside is, you have to create as many classes as there are stateA and stateB combinations. If the total combinations are witin (say) twenty, then this approach is feasible, otherwise not.

inquisitive
  • 3,549
  • 2
  • 21
  • 47