「goto」ブードゥー教を避ける?


47

switchいくつかのケースを処理する構造があります。switch動作enumを組み合わせた値によって重複したコードの問題を提起しています:

// All possible combinations of One - Eight.
public enum ExampleEnum {
    One,
    Two, TwoOne,
    Three, ThreeOne, ThreeTwo, ThreeOneTwo,
    Four, FourOne, FourTwo, FourThree, FourOneTwo, FourOneThree,
          FourTwoThree, FourOneTwoThree
    // ETC.
}

現在、switch構造は各値を個別に処理します。

// All possible combinations of One - Eight.
switch (enumValue) {
    case One: DrawOne; break;
    case Two: DrawTwo; break;
    case TwoOne:
        DrawOne;
        DrawTwo;
        break;
     case Three: DrawThree; break;
     ...
}

そこにアイデアがあります。私は現在、これをスタックif構造に分割して、代わりに単一行での組み合わせを処理しています:

// All possible combinations of One - Eight.
if (One || TwoOne || ThreeOne || ThreeOneTwo)
    DrawOne;
if (Two || TwoOne || ThreeTwo || ThreeOneTwo)
    DrawTwo;
if (Three || ThreeOne || ThreeTwo || ThreeOneTwo)
    DrawThree;

これは非常に長い論理評価の問題を引き起こし、読みにくく、維持するのが困難です。これをリファクタリングした後、私は代替案について考え始めswitch、ケース間のフォールスルーを伴う構造のアイデアを考え始めました。

フォールスルーを許可しないためgoto、その場合はaを使用する必要がありますC#。ただし、switch構造内でジャンプしても、信じられないほど長いロジックチェーンを防止し、コードの重複をもたらします。

switch (enumVal) {
    case ThreeOneTwo: DrawThree; goto case TwoOne;
    case ThreeTwo: DrawThree; goto case Two;
    case ThreeOne: DrawThree; goto default;
    case TwoOne: DrawTwo; goto default;
    case Two: DrawTwo; break;
    default: DrawOne; break;
}

これはまだ十分にクリーンなソリューションではなく、goto回避したいキーワードに関連したスティグマがあります。これをきれいにするもっと良い方法が必要だと思います。


私の質問

読みやすさと保守性に影響を与えずにこの特定のケースを処理するより良い方法はありますか?


28
大きな議論をしたいようです。ただし、gotoを使用するには、良いケースのより良い例が必要です。フラグ列挙型は、きちんとあなたの文の例は、許容可能な波平、それは私には罰金に見える場合も、これを解決
ユアン・

5
@Ewan誰もgotoを使用しません。Linuxカーネルソースコードを最後に見たのはいつですか?
ジョンドゥーマ

6
あなたは使用しgoto、高レベルの構造は、あなたの言語に存在しない場合。私は時々あったことを望むfallthru; その特定の使用法を取り除くためのキーワードですgotoが、まあまあです。
ジョシュア

25
一般的に、gotoC#のような高レベル言語でを使用する必要があると感じた場合、他の多くの(そしてより良い)設計や実装の代替案を見落としている可能性があります。非常にがっかりしました。
code_dredd

11
C#で「goto case」を使用することは、より一般的な「goto」を使用することとは異なります。これは、明示的なフォールスルーを実現する方法だからです。
ハンマーライト

回答:


175

gotoステートメントではコードが読みにくいと思います。構造をenum変えることをお勧めします。たとえばenum、各ビットが選択肢の1つを表すビットフィールドの場合、次のようになります。

[Flags]
public enum ExampleEnum {
    One = 0b0001,
    Two = 0b0010,
    Three = 0b0100
};

Flags属性は、重複しない値を設定していることをコンパイラーに伝えます。このコードを呼び出すコードは、適切なビットを設定できます。次に、このようなことをして、何が起こっているかを明確にすることができます。

if (myEnum.HasFlag(ExampleEnum.One))
{
    CallOne();
}
if (myEnum.HasFlag(ExampleEnum.Two))
{
    CallTwo();
}
if (myEnum.HasFlag(ExampleEnum.Three))
{
    CallThree();
}

これには、myEnumビットフィールドを適切に設定し、フラグ属性でマークするようにセットアップするコードが必要です。しかし、あなたの例の列挙型の値を次のように変更することでそれを行うことができます。

[Flags]
public enum ExampleEnum {
    One = 0b0001,
    Two = 0b0010,
    Three = 0b0100,
    OneAndTwo = One | Two,
    OneAndThree = One | Three,
    TwoAndThree = Two | Three
};

フォーム0bxxxxに数字を書くとき、それをバイナリで指定しています。したがって、ビット1、2、または3を設定していることがわかります(まあ、技術的には0、1、または2ですが、あなたは考えを知っています)。組み合わせが頻繁に一緒に設定される可能性がある場合は、ビットごとのORを使用して組み合わせに名前を付けることもできます。


5
それはそう表示されます!しかし、それはC#7.0以降でなければなりません。
user1118321

31
とにかく、次のようにすることもできますpublic enum ExampleEnum { One = 1 << 0, Two = 1 << 1, Three = 1 << 2, OneAndTwo = One | Two, OneAndThree = One | Three, TwoAndThree = Two | Three };。C#7 +を主張する必要はありません。
デデュプリケーター


13
この[Flags]属性はコンパイラに何も通知しません。あなたはまだ明示的に2のべき乗として列挙値を宣言する必要が理由です
ジョー・シーウェル

19
@UKMonkey私は激しく反対します。 HasFlagは、実行される操作を説明する明示的な用語であり、機能から実装を抽象化します。&他の言語で一般的であるために使用することはbyte、を宣言する代わりに型を使用することよりも意味がありませんenum
BJマイヤーズ

136

IMOの問題の根本は、このコードが存在すらしてはならないということです。

明らかに、3つの独立した条件と、それらの条件が真である場合に行う3つの独立したアクションがあります。では、なぜそれらすべてが何をすべきか(列挙に難読化するかどうか)を伝えるために3つのブールフラグを必要とする1つのコードに集中し、次に3つの独立したことを組み合わせますか?単一の責任原則は、ここで休みを取っているようです。

属する3つの関数(つまり、アクションを実行する必要があることがわかった場所)への呼び出しを行い、これらの例のコードをごみ箱に委託します。

3つではなく10のフラグとアクションがあった場合、この種類のコードを拡張して1024の異なる組み合わせを処理しますか?私は望んでいない!1024が多すぎる場合、同じ理由で8も多すぎます。


10
実際のところ、すべての種類のフラグ、オプション、およびその他のさまざまなパラメーターを備えた、適切に設計された多くのAPIがあります。SRPを破壊することなく、一部は直接の影響を受け、一部は直接的な効果がありますが、無視される可能性もあります。とにかく、関数は外部入力をデコードすることさえできます。また、特に出発点がそれほど堅実でもない場合、不合理な不合理はしばしば不合理な結果につながります。
デデュプリケーター

9
この答えを支持しました。GOTOを議論したいだけなら、それはあなたが尋ねた質問とは異なる質問です。提示された証拠に基づいて、3つのばらばらの要件がありますが、それらは集約されるべきではありません。SEの観点からは、alephzeroが正しい答えだと主張します。

8
@Deduplicator 1,2&3のすべてではないがいくつかの組み合わせのこのミッシュマッシュを見ると、これが「適切に設計されたAPI」ではないことがわかります。
user949300

4
そんなに。他の答えは、問題の内容を実際には改善しません。このコードは書き直す必要があります。より多くの関数を書くことに何の問題もありません。
only_pro

3
特に「1024が多すぎると、8も多すぎる」という答えが大好きです。しかし、本質的には「ポリモーフィズムを使用する」ですね。そして、私の(より弱い)答えは、それを言うためにたくさんのがらくたを得ています。広報担当者を雇うことはできますか?:-)
user949300

29

gotoを使用しないことは、コンピューターサイエンスの「子供たちへの嘘」の概念の1つです。99%の確率で正しいアドバイスが得られますが、それほど珍しくなく特殊なものでもないので、新しいコーダーに「使用しない」と説明した方がはるかに優れています。

それで、いつそれらを使うべきですか?いくつかのシナリオがありますがヒットしていると思われる基本的なシナリオ、ステートマシンをコーディングするときです。ステートマシンよりもアルゴリズムの体系的で構造化された表現が優れていない場合、コード内のその自然な表現には非構造化ブランチが含まれ、おそらく構造を構築しないものについてできることは多くありませんステートマシン自体が少なくなるのではなく、より不明瞭になります。

コンパイラの作家はLALRパーサを実装するほとんどのコンパイラのソースコードが理由である、このことを知っている* GOTOSが含まれています。ただし、実際に独自の字句解析器および構文解析器をコーディングする人はほとんどいません。

*-IIRCでは、ジャンプテーブルやその他の非構造化制御ステートメントを使用せずに、完全に再帰下降するLALL文法を実装することができます。したがって、本当にアンチゴートなら、これは1つの方法です。


次の質問は、「この例はそれらのケースの1つですか?」です。

私が見ているのは、現在の状態の処理に応じて、次の3つの異なる可能な状態があるということです。それらの1つ(「デフォルト」)は単なるコード行であるため、技術的には、該当する状態の最後にそのコード行を追加することで、それを取り除くことができます。それにより、次の2つの状態に進むことができます。

残りの1つ(「3つ」)は、私が見ることができる1つの場所からのみ分岐しています。したがって、同じ方法で取り除くことができます。次のようなコードになります。

switch (exampleValue) {
    case OneAndTwo: i += 3 break;
    case OneAndThree: i += 4 break;
    case Two: i += 2 break;
    case TwoAndThree: i += 5 break;
    case Three: i += 3 break;
    default: i++ break;
}

ただし、これはあなたが提供したおもちゃの例です。「デフォルト」に重要なコードが含まれる場合、「3」が複数の状態から移行されるか、(最も重要なことですが)さらなる保守により状態が追加または複雑になる可能性が高い場合、正直に言って良いでしょうgotoを使用します(そして、何らかの理由でそれを維持する必要のある正当な理由がない限り、物事のステートマシンの性質を隠すenum-case構造を取り除きます)。


2
@PerpetualJ-まあ、確かに何かもっときれいなものを見つけてくれてうれしいです。それが本当にあなたの状況であるなら、goto(ケースまたは列挙型ではなく、ラベル付きブロックとgotoのみ)で試して、読みやすさでそれらがどのように比較されるかを見るのは、まだ面白いかもしれません。そうは言っても、「goto」オプションは読みやすくする必要はありませんが、他の開発者からの引数や手直しの「修正」試行を食い止めるのに十分なほど読みやすいので、最良のオプションを見つけたようです。
TED

4
「さらなるメンテナンスが状態を追加または複雑にする可能性が高い」場合、「gotoを使用する方が良い」とは思わない。スパゲッティコードは構造化コードよりも維持しやすいと本当に主張していますか?これは、約40年の実践に反します。
user949300

5
@ user949300-ステートマシンの構築に対する練習ではありません。現実世界の例については、この回答に関する私の以前のコメントを読んでください。Cケースのフォールスルーを使用して、そのような制限が本質的にない状態マシンアルゴリズムに人工構造(線形順序)を課そうとすると、すべての再配置が必要になるたびに幾何学的に複雑さが増します新しい状態に対応するための注文。アルゴリズムが要求するような状態と遷移をコーディングするだけでは、それは起こりません。新しい状態を追加するのは簡単です。
TED

8
@ user949300-繰り返しになりますが、コンパイラを構築する「〜40年の実践」では、gotoが実際にその問題ドメインの一部に最適な方法であることが示されています。
TED

3
@ user949300特定の関数または規則を使用する場合、スパゲッティコードは本質的に表示されるだけではありません。任意の言語、機能、または規則でいつでも表示できます。原因は、意図されていないアプリケーションで上記の言語、機能、または規則を使用し、それを引き戻すために後方に曲げることです。仕事には常に適切なツールを使用しgotoます。はい、時にはを使用することを意味します。
Abion47

26

最良の答えは、ポリモーフィズムを使用することです。

IMOがifをより明確にし、間違いなく短くする別の答え:

if (One || OneAndTwo || OneAndThree)
  CallOne();
if (Two || OneAndTwo || TwoAndThree)
  CallTwo();
if (Three || OneAndThree || TwoAndThree)
  CallThree();

goto おそらくここで私の58番目の選択です...


5
ポリモーフィズムを提案するための+1。理想的には、クライアントコードを単純に「Call();」に単純化し、tell do n't askを使用して、決定ロジックを消費クライアントからクラスメカニズムおよび階層にプッシュします。
エリック・エイト

12
目に見えない最高の光景を宣言することは、確かに非常に勇敢です。しかし、追加情報がなければ、少し懐疑心を持ち、心を開いておくことをお勧めします。おそらくそれはコンビナトリアル爆発に苦しんでいますか?
デデュプリケーター

うわー、多型を示唆することに私がこれまでに反対票を投じたのはこれが初めてだと思います。:-)
user949300

25
一部の人々は、問題に直面したとき、「ポリモーフィズムを使用するだけです」と言います。現在、それらには2つの問題とポリモーフィズムがあります。
モニカとの軽さレース

8
実際に、ランタイムポリモーフィズムを使用してコンパイラのステートマシンのgotoを削除するという修士論文を作成しました。これはかなり実行可能ですが、実際にはほとんどの場合、努力する価値がないことがわかりました。大量のオブジェクト指向セットアップコードが必要です。もちろん、それらはすべてバグで終わる可能性があります(この回答では省略されています)。
TED

10

これはなぜですか:

public enum ExampleEnum {
    One = 0, // Why not?
    OneAndTwo,
    OneAndThree,
    Two,
    TwoAndThree,
    Three
}
int[] COUNTS = { 1, 3, 4, 2, 5, 3 }; // Whatever

int ComputeExampleValue(int i, ExampleEnum exampleValue) {
    return i + COUNTS[(int)exampleValue];
}

OK、私は同意します、これはハックです(私はC#開発者ではないので、コードを失礼します)が、効率の観点からこれは必須ですか?配列インデックスとして列挙型を使用することは有効なC#です。


3
はい。コードをデータで置き換える別の例(速度、構造、保守性のために通常望ましい)。
ピーター-モニカの復職

2
それは間違いなく、最新版の質問にとって素晴らしいアイデアです。また、最初の列挙型(値の密な範囲)から、一般的に実行する必要がある多かれ少なかれ独立したアクションのフラグに移動するために使用できます。
デデュプリケーター

私はC#コンパイラにアクセスできませんが、この種のコードを使用してコードをより安全にすることができint[ExempleEnum.length] COUNTS = { 1, 3, 4, 2, 5, 3 };ます。
ローラングレゴワール

7

フラグを使用できない、または使用したくない場合は、末尾再帰関数を使用します。64ビットリリースモードでは、コンパイラはgotoステートメントに非常によく似たコードを生成します。対処する必要はありません。

int ComputeExampleValue(int i, ExampleEnum exampleValue) {
    switch (exampleValue) {
        case One: return i + 1;
        case OneAndTwo: return ComputeExampleValue(i + 2, ExampleEnum.One);
        case OneAndThree: return ComputeExampleValue(i + 3, ExampleEnum.One);
        case Two: return i + 2;
        case TwoAndThree: return ComputeExampleValue(i + 2, ExampleEnum.Three);
        case Three: return i + 3;
   }
}

それはユニークなソリューションです!+1この方法で行う必要はないと思いますが、将来の読者には最適です!
PerpetualJ

2

受け入れられた解決策は問題なく、問題に対する具体的な解決策です。ただし、別のより抽象的なソリューションを提案したいと思います。

私の経験では、ロジックのフローを定義するために列挙型を使用することは、多くの場合、クラス設計が不十分であることの兆候であるため、コード臭です。

昨年私が取り組んだコードでこれが起こっているという実世界の例に出会いました。元の開発者は、インポートとエクスポートの両方のロジックを実行する単一のクラスを作成し、列挙型に基づいて2つのクラスを切り替えました。これで、コードは似ており、重複するコードがいくつかありましたが、コードが非常に読みにくく、テストが事実上不可能になるほど十分に異なっていました。最終的にそれを2つの別々のクラスにリファクタリングしました。これにより、両方が単純化され、実際に報告されていない多くのバグを見つけて排除することができました。

繰り返しになりますが、列挙型を使用してロジックのフローを制御することは、多くの場合設計上の問題であると述べなければなりません。一般的な場合、列挙型は主に、可能な値が明確に定義されているタイプセーフで消費者に優しい値を提供するために使用する必要があります。ロジック制御メカニズムとしてよりも、プロパティとして(たとえば、テーブルの列IDとして)使用する方が適切です。

質問で提示された問題を考えてみましょう。ここでのコンテキストや、この列挙型が何を表しているのかは本当にわかりません。絵カードですか?絵を描く?血を引く?順序は重要ですか?また、パフォーマンスがどれほど重要かわかりません。パフォーマンスまたはメモリが重要な場合、このソリューションはおそらくあなたが望むものではないでしょう。

いずれにせよ、列挙型を考えてみましょう:

// All possible combinations of One - Eight.
public enum ExampleEnum {
    One,
    Two,
    TwoOne,
    Three,
    ThreeOne,
    ThreeTwo,
    ThreeOneTwo
}

ここにあるのは、さまざまなビジネス概念を表すさまざまな列挙値です。

代わりに使用できるのは、物事を単純化するための抽象化です。

次のインターフェイスを考えてみましょう。

public interface IExample
{
  void Draw();
}

次に、これを抽象クラスとして実装できます。

public abstract class ExampleClassBase : IExample
{
  public abstract void Draw();
  // other common functionality defined here
}

描画1、2、3を表す具体的なクラスを持つことができます(引数のために異なるロジックがあります)。これらは上記で定義された基本クラスを使用する可能性がありますが、DrawOneの概念は、enumで表される概念とは異なると想定しています。

public class DrawOne
{
  public void Draw()
  {
    // Drawing logic here
  }
}

public class DrawTwo
{
  public void Draw()
  {
    // Drawing two logic here
  }
}

public class DrawThree
{
  public void Draw()
  {
    // Drawing three logic here
  }
}

そして今、他のクラスのロジックを提供するために構成できる3つの個別のクラスがあります。

public class One : ExampleClassBase
{
  private DrawOne drawOne;

  public One(DrawOne drawOne)
  {
    this.drawOne = drawOne;
  }

  public void Draw()
  {
    this.drawOne.Draw();
  }
}

public class TwoOne : ExampleClassBase
{
  private DrawOne drawOne;
  private DrawTwo drawTwo;

  public One(DrawOne drawOne, DrawTwo drawTwo)
  {
    this.drawOne = drawOne;
    this.drawTwo = drawTwo;
  }

  public void Draw()
  {
    this.drawOne.Draw();
    this.drawTwo.Draw();
  }
}

// the other six classes here

このアプローチははるかに冗長です。しかし、それには利点があります。

バグを含む次のクラスを検討してください。

public class ThreeTwoOne : ExampleClassBase
{
  private DrawOne drawOne;
  private DrawTwo drawTwo;
  private DrawThree drawThree;

  public One(DrawOne drawOne, DrawTwo drawTwo, DrawThree drawThree)
  {
    this.drawOne = drawOne;
    this.drawTwo = drawTwo;
    this.drawThree = drawThree;
  }

  public void Draw()
  {
    this.drawOne.Draw();
    this.drawTwo.Draw();
  }
}

欠落しているdrawThree.Draw()呼び出しを見つけるのはどれくらい簡単ですか?また、順序が重要な場合は、描画呼び出しの順序も非常に簡単に確認および追跡できます。

このアプローチの欠点:

  • 提示された8つのオプションのすべてに、個別のクラスが必要です
  • これはより多くのメモリを使用します
  • これにより、コードが表面的に大きくなります
  • 時々、このアプローチは不可能ですが、それに対するいくつかのバリエーションがあります

このアプローチの利点:

  • これらの各クラスは完全にテスト可能です。なぜなら
  • 描画メソッドの循環的複雑度は低いです(理論的には、必要に応じてDrawOne、DrawTwo、またはDrawThreeクラスをモックできます)。
  • 描画メソッドは理解可能である-開発者はメソッドが何をするのかを理解するためにノットに頭を縛る必要はない
  • バグは見つけやすく、書くのが難しい
  • クラスはより高いレベルのクラスに構成されます。つまり、ThreeThreeThreeクラスの定義は簡単です。

複雑なロジック制御コードをcaseステートメントに記述する必要があると感じるときはいつでも、この(または同様の)アプローチを検討してください。将来はあなたが幸せになります。


これは非常によく書かれた答えであり、私はこのアプローチに完全に同意します。私の特定のケースでは、この冗長性は、それぞれの背後にある些細なコードを考慮すると、無限に過剰になりすぎます。遠近感を出すために、コードが単にConsole.WriteLine(n)を実行していると想像してください。これは、実際に私が作業していたコードが実行していたことと同等です。他のすべてのケースの90%で、OOPに従う場合のソリューションが間違いなくベストアンサーです。
PerpetualJ

ええ、公正な点、このアプローチはあらゆる状況で役立つわけではありません。開発者が問題を解決するための特定のアプローチに固執するのが一般的であり、時には一歩下がって代替案を探すことになるので、ここに置きたかったのです。
スティーブン

私はこれに完全に同意します。実際に私がここで終わったのは、別の開発者が習慣から書いたものでスケーラビリティの問題を解決しようとしていたからです。
PerpetualJ

0

ここでスイッチを使用する場合は、各ケースを個別に処理するとコードが実際に高速になります

switch(exampleValue)
{
    case One:
        i++;
        break;
    case Two:
        i += 2;
        break;
    case OneAndTwo:
    case Three:
        i+=3;
        break;
    case OneAndThree:
        i+=4;
        break;
    case TwoAndThree:
        i+=5;
        break;
}

それぞれの場合に単一の算術演算のみが実行されます

また、gotoの使用を検討している場合は、他の人が述べているように、おそらくアルゴリズムを再考する必要があります(gotoを使用する理由かもしれませんが、C#のケースフォールがないことは認めます)。エドガー・ダイクストラの有名な論文「God Statement考慮された有害」を参照してください


3
これは簡単な問題に直面している人にとっては素晴らしいアイデアだと思うので、投稿してくれてありがとう。私の場合、それはそれほど単純ではありません。
PerpetualJ

また、エドガーが論文執筆時に抱えていた今日の問題は正確にはありません。最近のほとんどの人は、gotoを嫌う正当な理由すら持っておらず、コードを読みにくくしています。まあ、正直に言うと、もしそれが悪用されていたら、はい、そうでなければ、含まれているメソッドにトラップされているので、実際にスパゲッティコードを作成することはできません。言語の機能を回避するために努力するのはなぜですか?gotoは古風で、99%のユースケースで他のソリューションを検討する必要があると思います。しかし、それが必要な場合、それが目的です。
PerpetualJ

多くのブール値がある場合、これはうまくスケーリングしません。
user949300

0

あなたの特定の例では、列挙から本当に必要なのは各ステップのdo / do-notインジケータであったため、3つのifステートメントを書き換えるソリューションはa switchよりも好ましく、受け入れられた答えにした方が良いです。

しかし、それほどきれいに動作しないより複雑なロジックがある場合gotoswitchステートメントのsは紛らわしいと感じます。私はむしろこのようなものを見たいです:

switch (enumVal) {
    case ThreeOneTwo: DrawThree; DrawTwo; DrawOne; break;
    case ThreeTwo:    DrawThree; DrawTwo; break;
    case ThreeOne:    DrawThree; DrawOne; break;
    case TwoOne:      DrawTwo; DrawOne; break;
    case Two:         DrawTwo; break;
    default:          DrawOne; break;
}

これは完璧ではありませんが、gotos よりもこの方法の方が良いと思います。イベントのシーケンスが非常に長く、互いに重複しているため、各ケースの完全なシーケンスを綴るのが実際には意味をなさない場合、gotoコードの重複を減らすためよりもサブルーチンを好むでしょう。


0

最近、gotoキーワードを嫌う理由が本当にあるのかどうかはわかりません。これは間違いなく古風で、99%のユースケースでは必要ありませんが、理由は言語の機能です。

gotoキーワードを嫌う理由は、次のようなコードです

if (someCondition) {
    goto label;
}

string message = "Hello World!";

label:
Console.WriteLine(message);

おっと!それは明らかに機能しません。message変数は、そのコードパスに定義されていません。したがって、C#はそれを通過しません。しかし、それは暗黙的かもしれません。検討する

object.setMessage("Hello World!");

label:
object.display();

そしてdisplay、その中にWriteLineステートメントがあると仮定します。

この種のバグはgoto、コードパスをわかりにくくするため、見つけにくい場合があります。

これは簡単な例です。実際の例はそれほど明白ではないと仮定します。label:との間に50行のコードがあるかもしれませんmessage

この言語はgoto、使用方法を制限することでこれを修正するのに役立ち、ブロックからのみ下降します。しかし、C#gotoはそのように制限されていません。コードを飛び越えることができます。さらに、を制限する場合はgoto、名前を変更することもできます。他の言語はbreak、ブロックから降りるために、番号(終了するブロックの数)またはラベル(ブロックの1つに)を使用します。

の概念gotoは、低レベルの機械語命令です。しかし、私たちがより高いレベルの言語を持っている理由は、可変スコープなどのより高いレベルの抽象化に限定されるためです。

つまりgotoswitchステートメント内でC#を使用してケースからケースにジャンプする場合、それは合理的に無害です。各ケースはすでにエントリポイントです。gotoこの無害な使用法gotoをより危険な形式で圧縮するため、それを呼び出すことはその状況ではばかげているとまだ思います。私はむしろ彼らcontinueがそのようなものを使うことを望んでいます。しかし、何らかの理由で、彼らは言語を書く前に私に尋ねることを忘れていました。


2
ではC#、言語は、変数の宣言を飛び越えることはできません。証明のために、コンソールアプリケーションを起動し、投稿で提供したコードを入力します。エラーが未割り当てのローカル変数 'message'を使用していることを示すコンパイラエラーがラベルに表示されます。これが許可されている言語では、それは有効な懸念事項ですが、C#言語ではそうではありません。
PerpetualJ

0

このように多くの選択肢がある場合(そしてあなたが言うように、さらに多くの選択肢がある場合)、それはおそらくコードではなくデータです。

列挙値をアクションにマッピングするディクショナリを作成します。ディクショナリは、関数またはアクションを表す単純な列挙型として表されます。次に、コードを単純な辞書検索に要約し、関数値を呼び出すか、単純化された選択肢を切り替えることができます。


-7

ループと、breakとcontinueの違いを使用します。

do {
  switch (exampleValue) {
    case OneAndTwo: i += 2; break;
    case OneAndThree: i += 3; break;
    case Two: i += 2; continue;
    case TwoAndThree: i += 2;
    // dropthrough
    case Three: i += 3; continue;
    default: break;
  }
  i++;
} while(0);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.