Jump構文(goto)を理解しよう!

[C#] Jump構文(goto)を理解しよう!

※ 当サイトは広告を含みます。

ここでは最後の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 (フォールスルー)が禁止されてましたよね。

にも関わらず、遥かに危険なgotoが禁止されてません。何故でしょうか。
それは使ったほうがいい場面が存在するからです。

goto文の利用例

具体例として次の2つは利用しても良いと思ってます。

  1. switch文で別のcaseに移動
  2. 多重ループの脱出

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#に関する学習コンテンツ

この記事は参考になりましたか?

関連記事

コメント

この記事へのコメントはありません。