goto identifier goto case constant-expression goto default
In the first usage of the goto statement here, the target of the identifer is a label statement. The label statement takes the form -
identifer: -
If that label doesn't exist in the current method, a compile-time error will result. Another important rule to remember is that the goto statement can be used to jump out of a nested loop. However, if the goto statement is not within the scope of the label, a compile-time error will result. Therefore, you cannot jump into a nested loop.
In the following example, the application is iterating through a simple array, reading each value until it reaches a sentinel value whereupon it exits the loop. Notice that the goto statement really just acts like the break statement in that it causes the program flow to jump out of the foreach loop.
using System; using System.Collections; class MyArray { public ArrayList words; public const string TerminatingWord = "stop"; public MyArray() { words = new ArrayList(); for (int i = 1; i <= 5; i++) words.Add(i.ToString()); words.Add(TerminatingWord); for (int i = 6; i <= 10; i++) words.Add(i.ToString()); } } class Goto1App { public static void Main() { MyArray myArray = new MyArray(); Console.WriteLine("Processing array..."); foreach (string word in myArray.words) { if (word == MyArray.TerminatingWord) goto finished; Console.WriteLine(word); } finished: Console.WriteLine("Finished processing array"); } }
Regarding this use of the goto statement, one could argue that a break statement could have been used just as effectively and a label wouldn't have been necessary. We'll look at the other forms of the goto statement, and you'll see that the problems at hand can be resolved only with the goto statement.
In the section on the switch statement, I discussed the fact that fall-throughs are not supported in C#. Even if fall-throughs were supported, it wouldn't solve the following problem. Let's say we have a Payment class (reused from earlier in the chapter) that accepted several forms of payment, or tenders: Visa, American Express, MasterCard, cash, and charge off (basically a credit). Because Visa, American Express, and MasterCard are all credit cards, we could combine them into a single case label and process them all in the same manner. In the case of the charge off, we would need to call methods specific to it, and in the case of the cash purchase, we'd only want to print a receipt. Also, we would want to print a receipt in all cases. How could we have three distinct case labels but have the first two cases-the credit card and the charge back cases-both branch to the cash label when done? As you can see in the following code, this problem is a good example of when to use the goto statement: -
using System; enum Tenders : int { ChargeOff, Cash, Visa, MasterCard, AmericanExpress }; class Payment { public Payment(Tenders tender) { this.Tender = tender; } protected Tenders tender; public Tenders Tender { get { return this.tender; } set { this.tender = value; } } protected void ChargeOff() { Console.WriteLine("Charge off."); } protected bool ValidateCreditCard() { Console.WriteLine("Card approved."); return true; } protected void ChargeCreditCard() { Console.WriteLine("Credit Card charged"); } protected void PrintReceipt() { Console.WriteLine("Thank you and come again."); } public void ProcessPayment() { switch ((int)(this.tender)) { case (int)Tenders.ChargeOff: ChargeOff(); goto case Tenders.Cash; case (int)Tenders.Visa: case (int)Tenders.MasterCard: case (int)Tenders.AmericanExpress: if (ValidateCreditCard()) ChargeCreditCard(); goto case Tenders.Cash; case (int)Tenders.Cash: PrintReceipt(); break; default: Console.WriteLine("\nSorry - Invalid tender."); break; } } } class GotoCaseApp { public static void Main() { Payment payment = new Payment(Tenders.Visa); payment.ProcessPayment(); } }
Instead of having to solve the problem counterintuitively, we simply tell the compiler that when a credit card or charge off case is finished, we want to branch to the cash label. One last thing to note here is that if you branch out of a case label in C#, you should not use a break statement, which would result in a compiler error for "unreachable code." -
The last form of the goto statement enables you to branch to the default label in a switch statement, thereby giving you another means of writing a single code block that can be executed as a result of multiple evaluations of the switch statement.