跳转到代码C#中的某一行

时间:2017-08-09 08:18:33

标签: c# goto

我有一些方法可以从其他一些方法中调用。在某些方法中执行某个操作时,我想回到第一个方法并跳过剩下的代码。目前,我使用booleans检查"状态"该程序,但我想避免这种情况,因为方法应该是void,因为从本质上讲它们不需要返回任何东西。我发现像goto这样的东西,但只能在同一种方法中使用。

问题:有没有办法在C#中以不同的方法跳转到代码中的特定点?我在其他语言上找到了东西,但在C#上找不到很多东西。

现状:

    void test1()
    {
        bool status = test2();

        if (!status)
            return; // the other stuff will not get done

        Debug.WriteLine("Initialization OK");
    }

    bool test2()
    {
        bool status = test3();

        if (!status)
            return false; // the other stuff will not get done

        // do other stuff 
        return true;
    }

    bool test3()
    {
        if (xxx)
            return false; // the other stuff will not get done
        else
            // do other stuff 
            return true;
    }

通缉情况:

    void test1()
    {
        test2();

        // do other stuff
        Debug.WriteLine("Initialization OK");

        GOTOHERE:
             Debug.WriteLine("Initialization NOT OK");
    }

    void test2()
    {
        test3();            
        // do other stuff 
    }

    void test3()
    {
        if (xxx)
            **GOTOHERE**; // Go directly to the location in test1() so that all unnecessary code is skipped

        // do other stuff
    }

7 个答案:

答案 0 :(得分:6)

我很惊讶地发现C#确实支持GOTO命令。但它旨在允许从深嵌套循环中退出。

本文对此进行了解释并提供了大量示例:https://www.dotnetperls.com/goto

<强>然而

除非你在1970年仍然编码,否则使用GOTO被认为是非常糟糕的做法。它使代码维护非常困难。它甚至会导致问题和性能问题,并使JIT编译器的生命变得更加困难。

  

现在的陈述过于原始,太过分了   邀请弄乱一个人的节目。

     

Edsger W. Dijkstra

答案 1 :(得分:2)

从您的方法中返回一些内容以表明您之后应该做什么完全您应该做什么。因此返回一个布尔值,指示test2test3是否成功,并使用该值指示是否要继续进行。现在不要使用goto,因为它只能用于意大利面条代码,这很难维护。要确定控制流应在何种情况下跳转到GOTOHERE,您需要扫描整个代码以查找特定的goto语句。

在您的情况下,您想要指出某些初始化代码是否正常工作。因此,您也可以抛出异常:

void test3()
{
    if (xxx)
        throw new Exception("Some text");

    // do other stuff
}

这样您就不需要从方法中返回任何内容,而是适当地处理异常:

void test1()
{
    try { test2(); }
    catch { 
        // some exception-handling such as logging
        return; 
    }

    Debug.WriteLine("Initialization OK");
}

这样做的好处是,如果test2成功,您就不需要签入test3,这样您就可以让异常通过通过方法,直到最后由catch处理。如果在整个callstack中找不到catch,您的应用可能会终止。

答案 2 :(得分:2)

C#确实有goto个关键字,与其他语言一样。将标签声明为label_you_want_to_jump_to:,并使用goto label_you_want_to_jump_tohttps://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/goto)。

既然这样说,使用goto通常是一个坏主意,特别是以这种方式。通过重构可以更轻松地解决您的问题。将test3拆分为两个函数,减少嵌套量可能就是这样。您也可以在test3中抛出异常,并在test1中捕获它。这一切都取决于你想做什么。

答案 3 :(得分:1)

如果你摆脱了明显的bool(这是多余的),你的(相当做作的)例子在我看来看起来很“干净”:

void test1()
{
    if (test2())
    {
        Debug.WriteLine("Initialization OK");
    }
}

bool test2()
{
    return test3();
}

bool test3()
{
    return xxx;
}

我也更喜欢使用正面而非负面条件,所以“if(true)”而不是“if(!false)”。这避免了双重否定,这很难理解。

实际上我们正在使用Predicate Logic。我们可以使用普通逻辑运算符组合我们的谓词(方法而不是返回bool而没有副作用)。考虑这个例子:

bool test4()
{
    if (test1())
    {
        if (test2())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

我们注意到这只是test1和test2的逻辑连接(和),所以可以简单地使用&amp;&amp;连接算子使这个更清晰:

bool test4()
{
    return test1() && test2();
}

类似于逻辑分离(或):

bool test5()
{
    if (test1())
    {
        return true;
    }
    else if (test2())
    {
        return true;
    }
    else 
    {
        return false;
    }
}

我们可以将其改为:

bool test5()
{
    return test1() || test2();
}

答案 4 :(得分:0)

我不推荐它,但问题的简单答案是使用goto声明。

请参阅:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/goto

但是,您应该从以前的方法调用中返回值,以确定是否需要运行其他代码。

你也可以throw例外,但总的来说我不喜欢用它们来控制流量,除非情况特殊(因此我想这个名字)。如果您希望控制可能以这种方式流动,那也不例外。

答案 5 :(得分:0)

我理解你的观点但是打破事件的顺序(又名跳跃)并不是一种好的做法。您的代码变得不可读,因为读者需要从一个地方跳到另一个地方。有一个原因你不能跳出当前的方法:

以下是真正简化的解释。我对许多细节都很兴奋 如果您了解how the stack works,您将知道运行时将为每个新方法调用将新的堆栈帧推送到堆栈中。新的堆栈帧包含方法的局部变量以及其他实现细节。如果要跳转到另一个方法,运行时需要将新的堆栈帧推送到堆栈中,以便为该方法创建那些局部变量。所以你的goto将成为方法调用而不是跳转语句。这很奇怪,而不是你/我们想要的。在这种情况下使用常规方法调用而不是跳转语句。

returnbreakcontinue之类的跳跃声明。但goto不是其中之一,尽管我认为{{1}在某些情况下是一个有效的解决方案。

返回有关下一步操作的信息是正确的行为。
我确实同意从回归的角度来看,返回一个bool并不是很有表现力,至少不是你当前的方法名称。命名改进建议:

goto case

答案 6 :(得分:0)

FYI

row.SetValues(new object[]{hotelId, RequestRef, info.FromDate, info.ToDate, info.Nights, info.MealPlan, info.StarCategory, info.Status, info.LastUpdatedBy, DateTime.Now, DateTime.Now, info.CreatedUserId});