ここでは最後のJump statementのgoto文について学びましょう。
goto statement (ステートメント)
このgoto文とは、全ての制約を無視して指定したラベルまでジャンプ(移動)できるヤバい構文です。
恐らく否定的な意見のほうが多く、僕もそうでしたが学校の授業なら100%使うなって教わります。
何故でしょうか?
それはプログラムの基本構造を無視して動作するからです。
では、実際にサンプルコードで動きを見てみましょう。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main: 開始");
goto LABEL; // LABEL: の位置にジャンプできる
Console.WriteLine("Main: 中間");
Console.WriteLine("Main: 中間");
Console.WriteLine("Main: 中間");
Console.WriteLine("Main: 中間");
Console.WriteLine("Main: 中間");
LABEL: // ジャンプ先になるラベル
Console.WriteLine("Main: 終了");
}
}
}
どうですか。恐ろしい遷移をすると思いませんか?。念のため構文も確認しておきますかね。
ラベルは任意の名前に:(コロン)を利用して付けます。
ラベル名:
そしてgotoでジャンプ先を指定すると、指定した位置まで飛べます。
goto ラベル名;
禁止されるほど複雑かな?って思った人、次のサンプルコードを見てみましょう。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main: 開始");
goto LABEL1;
LABEL2:
Console.WriteLine("Main: LABEL2");
goto LABEL3;
LABEL4:
Console.WriteLine("Main: LABEL4");
goto LABEL5;
LABEL1:
Console.WriteLine("Main: LABEL1");
goto LABEL2;
LABEL3:
Console.WriteLine("Main: LABEL3");
goto LABEL4;
LABEL5:
Console.WriteLine("Main: LABEL5");
}
}
}
ヤバくないですか?
流石にこれはヤバい。
こういう記述が許可さてるので禁止派が多い印象です。では、本当に駄目なんでしょうか?
Not!先程の狂った使い方が禁止すべきであって、gotoの全てを禁止は無能の発想です。
確かに学校で教える先生の立場では対象は初心者なので、禁止したほうがいいに決まってます。
ですが、その話を鵜呑みにしたまま先に進み、条件反射で禁止っていうような低俗な人間になるのは駄目です。
どうしてそんな煽るような言い方しかできないんですか?
反抗的なやつが釣れたら面白いから。
性格おわってる...
本題に戻ると、そもそもgotoがC#で言語的に禁止されてないのは意味があります。
思い出してくださいswitch文でfall through (フォールスルー)が禁止されてましたよね。
ここではC#のswitch文について理解しましょう。これはプログラムの基本構造で説明した選択にあたる処理です。なお、プログラムの基本構造が分からない人は先に次の記事を読んでください。 switch statement (ステートメント) if文と同じく条件分岐の処理になります。基本的にはif文と同じなのですが、switch文のほうが高度な記述ができます。 パターン1: switch 定数値 最もシンプルな記述はこれです。 評価値に記述できるの...
にも関わらず、遥かに危険なgotoが禁止されてません。何故でしょうか。
それは使ったほうがいい場面が存在するからです。
goto文の利用例
具体例として次の2つは利用しても良いと思ってます。
- switch文で別のcaseに移動
- 多重ループの脱出
switch文で別のcaseに移動
これは以前にも伝えたとおりです。同スコープのswitchで別のcaseに飛ぶことは何ら問題ではありません。
gotoで指定するラベルもcase付きなので問題ないでしょう。
多重ループの脱出
次は多重ループをまとめて抜ける方法になります。
ループをbreakで抜けれることは解説済みですが、これは1ループしか抜けることができません。
下位のループで終了条件を満たした時に全体のループを抜けたくても、breakで多重ループの全てを抜けることはできないのです。
そんな場合によく利用されるのがフラグを使った脱出処理です。まずはサンプルコードを見てみましょう。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
int[,] array = new int[,]
{
{ 0, 0, 0, 0, 0},
{ 0, 0, 0, 1, 0},
{ 0, 0, 0, 0, 0},
};
bool flag = false;
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
Console.WriteLine($"[{i},{j}]={array[i, j]}");
if (array[i, j] == 1) // 値が1なら全体を抜けたい
{
flag = true;
break;
}
}
if (flag)
{
break;
}
}
}
}
}
では、これをgoto文で書き換えてみましょう。
using System.Reflection.Emit;
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
int[,] array = new int[,]
{
{ 0, 0, 0, 0, 0},
{ 0, 0, 0, 1, 0},
{ 0, 0, 0, 0, 0},
};
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
Console.WriteLine($"[{i},{j}]={array[i, j]}");
if (array[i, j] == 1) // 値が1なら全体を抜けたい
{
goto LOOPEND;
}
}
}
LOOPEND:
; // 空処理(下に処理が無い時は記述しないとエラーになる)
}
}
}
どうですか? 多重ループに関しては、これほど優れた書き方があるでしょうか。
これでもgoto文を否定するならどうぞ。たぶん、僕とは違う世界の生き物なんでしょうね。
色々な考え方がありますよ。
ちなみにラベルは:(コロン)だけだと次の行のコメントがズレるので、常に空処理で;(セミコロン)を書くといいですよ。
あとがき
僕は殆ど使わないのですが、何らかのエラー時に絶対に通らないと行けない処理がある場合もgoto文を許容するって考え方があります。
このように許容する人は必ずルールを付けてます。かなり危険な構文なので、自分なりのルールを作って利用することをオススメします。
◆ C#に関する学習コンテンツ
この記事は参考になりましたか?
コメント