【问题标题】:Using goto case(variable); in C# switch使用 goto case(variable);在 C# 中切换
【发布时间】:2013-10-10 15:21:42
【问题描述】:

我正在为 Visual Studio C# 控制台中的一门课程编写文本冒险,并决定使用 switch 语句而不是 if-else 链,因为 goto 案例非常有用(到目前为止,它的效果非常好)。我知道开关本身中的每个案例都必须是一个常数,但我想知道这是否也扩展到使用 goto 案例。例如我有:

switch (location)
{
case 1:
  break;
case 2:
  break;
case 3:
  break;
//I have 10 cases, each representing a location such as "Orc Cave", I just cut it down for brevity
default:
  break;
}

我希望能够输入一个整数变量,然后转到该整数,我有以下方法可以完成:

string travel2 = Console.ReadLine();//inputs a integer representing each location
int travel2A = Convert.ToInt32(travel2);
if (1<=travel2A && travel2A<=10)
{
    goto case(travel2A);
}
else{
    goto case(2);//current location
}

一切正常,但在案例 (travel2A) 下方显示“预期值为常数”的警告。是否可以通过一些调整使 goto case 输入变量,或者这只是 switch 语句的限制?如果是后者,我可以执行一系列 if-else,但在我看来,输入变量更方便。非常感谢您对此的任何帮助!谢谢!

【问题讨论】:

标签: c# switch-statement goto


【解决方案1】:

普通goto的标签需要在源代码中拼出。它们不是被评估的表达式;它们只是标识符goto case 的标签必须是常量表达式,在编译时计算,而不是在运行时计算。

我会提醒您不要使用任何严重依赖任何形式的“goto”的解决方案。对任何一种“goto”都有一种普遍的偏见。 gotos 被认为是不优雅的,并且使您的代码难以遵循。可以肯定的是,这种偏见背后有一些真相,尽管 C# 已经过精心设计,因此“goto”的最严重滥用是不可能或不可能的。 (C# 只允许在同一个块内或从内部块到其包含块之一的 goto。它不允许从外部块到内部块或在没有嵌套关系的两个块之间的 goto。这大大降低了可能性“意大利面条代码”。)

我假设由于您正在学习一门课程,因此您才刚刚开始学习 C#。随着您的进步,您将了解比打开案例更优雅的控制流技术。

当我们批评您的代码时:考虑使用int.TryParse 而不是Convert.ToInt32,并确保您处理用户键入的不是整数的情况。

【讨论】:

  • 谢谢!这只是我在 C# 中的第二周,所以我仍在熟悉它。我会切换到 if-else's 去旅行。感谢您的 int.TryParse 建议。我会马上做的。
  • @RussellDahlke:不客气。听起来您正在对控制流进行一些很好的思考。现在退后一步,问问自己是否可以更抽象地概念化你的流程。文字冒险游戏本质上是一个状态机。状态机具有以下流程:(1)创建开始状态,(2)获取输入,(3)根据当前状态和输入计算出对状态的更改,(4)如果新状态是停止state,结束程序,(5) goto 2。如果你能像这样构造你的程序,它可能更容易理解。显然(3)是最难的部分!
【解决方案2】:

看这个POST

基本上,开关不能在 case 语句中包含评估语句。必须对它们进行静态评估。

【讨论】:

    【解决方案3】:

    怎么样:

    int travel2A = Convert.ToInt32(travel2);
    if( travel2A < 1 || travel2A > 10 )
        travel2A = 2;
    
    switch( travel2A ) { ... }
    

    【讨论】:

      【解决方案4】:

      编译器错误A constant value is expected 说明了一切。

      在 C# 中无法做到这一点的原因(VB Select 的操作略有不同)在调试模式下运行时得到了很好的证明。每当您遇到switch 语句时,您会注意到代码将跳转到所选用例。虽然跳过多次比较可以显着提升性能,但它限制了灵活性。

      我过去解决这个问题的一种方法是使用匿名方法进行递归。因此,您可以使用重新调用switch 语句的方法调用,而不是使用goto case(travel2a)

      Action<int> foo = null;
      foo = (i) =>
      {
          switch (i)
          {
              case 1:
                  Console.WriteLine("1");
                  break;
              case 2:
                  Console.WriteLine("1");
                  break;
              case 3:
                  Console.WriteLine("1");
                  break;
              default:
                  //goto case (i%3);
                  foo(i % 3);
                  break;
          }
      };
      
      foo(4);
      

      【讨论】:

        【解决方案5】:

        您可以将case 代码组织为一个方法并简单地调用它:

        switch (location)
        {
            case 1:
                gocave();
                break;
            case 2:
                gocave();
                break;
            case 3:
                donotgocave();
                break;
        }
        

        或将复杂的逻辑拆分为原语:

        bool gocave = false;
        bool eatmushroom = false;
        switch (location)
        {
            case 1:
                gocave = true;
                break;
            case 2:
                gocave = true;
                eathmushroom = true;
                break;
            case 3:
                break;
        }
        if(gocave) {...}
        if(eatmushroom) {...}
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多