C# 清理c中的超粗箭头反模式#

C# 清理c中的超粗箭头反模式#,c#,if-statement,nested-loops,C#,If Statement,Nested Loops,我对编码相当陌生,我希望尽早改掉坏习惯,开始编写干净高效的代码。我正在开发一个引用API的控制台应用程序,我有一系列深度嵌套的“if”(最深10层!) 这只是ifs总巢的一个小样本。我已经做了很多关于清理这个的研究,我很难理解/使用我找到的答案。 我在重构中遇到的最大问题是,后面的每个if都直接依赖于前面的if 我还制作了一个逻辑表,列出了到达每个if所需的输入。如果有帮助,我会把它放在这里: 我非常感谢您的帮助,如果您能解释一下为什么您的答案可以提高可读性和效率,那也将是非常好的 看看这段代

我对编码相当陌生,我希望尽早改掉坏习惯,开始编写干净高效的代码。我正在开发一个引用API的控制台应用程序,我有一系列深度嵌套的“if”(最深10层!)

这只是
if
s总巢的一个小样本。我已经做了很多关于清理这个的研究,我很难理解/使用我找到的答案。 我在重构中遇到的最大问题是,后面的每个
if
都直接依赖于前面的
if

我还制作了一个逻辑表,列出了到达每个
if
所需的输入。如果有帮助,我会把它放在这里:

我非常感谢您的帮助,如果您能解释一下为什么您的答案可以提高可读性和效率,那也将是非常好的

看看这段代码:

bool loop = true;

while(loop)
{
    Console.WriteLine("Question");
    Console.WriteLine("1. Ans1");
    Console.WriteLine("2. Ans2");
    Console.WriteLine("3. Exit");
    string resp = Console.ReadLine();

    switch(resp)
    {
        case "1":
        Console.WriteLine("Ans1 chosen");
        break;
        case "2":
        SomeQuestion();
        break;
        case "3":
        loop = false;
        break;
        default:
        Console.WriteLine("Invalid Input. Please try Again");
        break;        
    }
}

void SomeQuestion()
{
    bool loop = true;

    while(loop)
    {
        Console.WriteLine("Question secon level");
        Console.WriteLine("1. Ans3");
        Console.WriteLine("2. Ans4");
        Console.WriteLine("3. Exit");
        string resp = Console.ReadLine();

        switch(resp)
        {
            case "1":
            Console.WriteLine("Ans1 chosen");
            break;
            case "2":
            break;
            case "3":
            loop = false;
            break;
            default:
            Console.WriteLine("Invalid Input. Please try Again");
            break;        
        }
    }    
}
这很简单,只有一个问题,但表明了想法

  • 取而代之的是使用条件进行循环
  • 相反,如果一个开关使代码更可读
我们先来分析一下眼前的问题

转到语句(错误):

此语句令人困惑,因为它不符合执行流程。它基本上是说“转到你必须找到的代码中的任意点”。现在乘以3或4,你就有了一个迷宫,里面有很多传送点。你会很快迷路的

以下是如何解决此问题的示例:

if(condition)
{
    try2:

    // do stuff

    if(condition2)
    {
        // Do more stuff
    }
    else
    {
        // Report error
        goto try2;
    }

}

// Replace with:

while(condition)
{
    // do stuff

    if(condition2)
    {
        // Do more stuff
        break;
    }
    else
    {
        // Report error
        continue; // Goes to the top of the loop
    }
}
这个例子并不完美,从技术上讲,您不需要“continue”,但我想解释一下它的用法

命名约定:

每个类名、变量名、方法名等都需要一个描述性标识符。“循环”什么也没告诉我。尽可能描述命名。如果这样做后仍然存在一些歧义,请在定义中使用注释来澄清

嵌套循环:

好的,这是你程序的总体设计。不幸的是,询问大量相关问题的控制台应用程序将变得一团糟

为什么?

因为WPF等用户界面框架可以更好地处理此类逻辑。例如,许多逻辑可以在按钮处理程序和控件属性中处理

但是,您仍然可以改进代码。但是,它需要非常面向对象的设计。我会选择一个状态模式作为开始

状态模式资源:

但是,这是高级的,如果您对类、方法、对象、属性、处理程序等没有太多经验,那么就不容易理解,而且它甚至不会减少您的工作量。事实上,它是为了可读性和可测试性而增加的,如果这是概念或实践的证明,您甚至可能不需要这两者


因此,我的建议是一步一步地掌握你的知识。开发最健壮的解决方案是好的,但如果您刚刚开始,就不行了。每次拿起一块,直到你有足够的经验把它们放在一起。

我知道你才刚刚开始,所以我不会因为你使用“goto”而对你大喊大叫,但不要使用“goto”,我能给你的最大提示就是永远不要使用“goto”。这个语句总是可以用某种循环来代替。学习循环的“中断”和“继续”关键字。另外,总是有意义地命名类和方法。“loop.loops1”既无用又令人困惑。@maccettura我有goto's,因此如果使用了无效的输入,它会将您带回到循环的开始。我知道它的坏习惯,但我不知道更好的方法。你也可以将其中的一些操作分解为处理每个“菜单”的无效方法。这样你的代码就不会一团糟,而是模块化的、整洁的。@RaptorJesus如果你知道这是一个糟糕的做法,那么你应该花更多的时间在教程上,退一步看看解决这个问题的其他方法。永远不要做不好的事情,因为你想不出更好的方法。找到更好的方法!我前面说过要走的路。将变量和方法命名为更具描述性的名称,将逻辑分解为若干部分并重新组织。让每个方法负责一件事(比如导航一个菜单)。这难道不会导致一堆嵌套的开关吗?这比嵌套ifs更糟糕还是更好?或者我会让案例“2”从这个时间段转换成另一个时间段来处理后续ifs块的逻辑吗?@RaptorJesus不幸的是,在控制台应用程序中,您会遇到很多嵌套问题。控制台应用程序对于很多用户交互来说都是糟糕的UI。最重要的一件事是彻底摆脱你的
goto
。如果你把东西分解成单独的方法来处理每个菜单,那么这就不会是一个混乱,它将是干净的。我认为开关比ifs更可读:-)我用第二级改进了我的示例,这样代码就更可读了。就像说@maccettura从不使用goto一样。@RaptorJesus的例子得到了改进,这样你总是有一个级别的切换,这是一个非常好的答案!谢谢你,我会调查这一切的。我同意控制台应用程序不是这个项目的最佳选择,毕竟在重新创建web应用程序方面。因此,我试图对通常以按钮或下拉菜单的形式处理的东西进行编码。但我没有将其作为web应用程序的技能或经验。@RaptorJesus制作web应用程序可以说比在控制台应用程序中编写更容易。查看ASP.NETMVC和引导程序。许多样板和脚手架为您处理了大量工作。不仅如此,控制台应用程序代码也不会移植到web应用程序中。无论如何,你都要重写它的绝大部分。如果你只是在你想要的平台上尝试,可能会更好。如果你不喜欢成品,
if(condition)
{
    try2:

    // do stuff

    if(condition2)
    {
        // Do more stuff
    }
    else
    {
        // Report error
        goto try2;
    }

}

// Replace with:

while(condition)
{
    // do stuff

    if(condition2)
    {
        // Do more stuff
        break;
    }
    else
    {
        // Report error
        continue; // Goes to the top of the loop
    }
}