练习 - 使用方法生成代码

已完成

方法对于组织代码、重用代码和有效地解决问题非常有用。 可以将方法想象成一个接受输入、执行指定任务并返回输出的黑盒。 根据此假设,只需将任务命名为方法,然后在确定所有必要的任务后填写逻辑,即可快速构建程序。

如果使用简单的语言来描述代码中的步骤,而不严格遵守语法规则,那么你就是在使用“伪代码”。 将方法和伪代码相结合这种办法非常适合用于快速完成任何具有挑战性的编程任务。

使用方法构建代码

假设你是编码面试的候选人。 面试官希望你编写一个程序来检查 IPv4 地址是有效还是无效。 规则如下:

  • 有效的 IPv4 地址由四个数字组成,以点分隔
  • 每个数字不得包含前导零
  • 每个数字的范围必须介于 0 到 255 之间

有效 IP 地址的示例:1.1.1.1 和 255.255.255.255。

IPv4 地址以字符串的形式提供。 可以假设它只包含数字和点(提供的字符串中没有字母)。

你将如何完成此任务?

注意

即使不熟悉 IP 地址,也不要担心! 你仍然可以按照步骤完成本练习中的代码。

拆分问题

在此任务中,你将确定解决问题所需的步骤。 如果仔细查看规则,可以发现只需三个步骤即可确定 IPv4 地址是否有效。

  1. 在 Visual Studio Code 编辑器中,删除前面练习中的任何现有代码。

  2. 在编辑器中输入以下伪代码:

    /*
    if ipAddress consists of 4 numbers
    and
    if each ipAddress number has no leading zeroes
    and
    if each ipAddress number is in range 0 - 255
    
    then ipAddress is valid
    
    else ipAddress is invalid
    */
    

    伪代码是开始处理任何问题的好方法。 使用此注释块,可以弥合提示规则和程序代码之间的差距,阐明代码将执行的主要任务。 伪代码无需正常运行或遵守语法规则,但它应该清楚地说明代码将执行的操作。 现在,让我们将其转换为真实代码!

  3. 输入新的空白代码行,然后在编辑器中键入以下代码:

    if (ValidateLength() && ValidateZeroes() && ValidateRange()) 
    {
        Console.WriteLine($"ip is a valid IPv4 address");
    } 
    else 
    {
        Console.WriteLine($"ip is an invalid IPv4 address");
    }
    

    在此步骤中,将伪代码中的 if 语句转换为可调用的方法并输出结果。 此时不用担心如何定义方法;可以假定每个方法都执行其名称所述的任务。 很快你将修复编译错误并创建方法逻辑,但现在要着眼于大局。 开始编写新程序时,专注于整体设计有助于保持井然有序并更快地开发应用程序。

  4. 在现有代码下方输入新的空白代码行,然后在编辑器中键入以下代码:

    void ValidateLength() {}
    void ValidateZeroes() {}
    void ValidateRange() {}
    

    注意,使用占位符方法可让你快速解决问题并构建代码来开发解决方案。 有了结构化的计划后,便可通过逐段填写代码来继续解决问题。

开发解决方案

现在,你已拥有解决问题所需的所有占位符方法,接下来可以开始关注解决方案的细节了。 请记住,IPv4 地址的输入格式将是一个由点分隔的数字组成的字符串。 让我们开始吧!

  1. 开始编写程序时,创建变量来存储输入和验证状态:

    string ipv4Input = "107.31.1.5";
    bool validLength = false;
    bool validZeroes = false;
    bool validRange = false;
    
  2. 更新解决方案代码以使用验证变量,如下所示:

    ValidateLength(); 
    ValidateZeroes(); 
    ValidateRange();
    
    if (validLength && validZeroes && validRange) 
    {
        Console.WriteLine($"ip is a valid IPv4 address");
    } 
    else
    {
        Console.WriteLine($"ip is an invalid IPv4 address");
    }
    
  3. 更新 ValidateLength 方法,如下所示:

    void ValidateLength() 
    {
        string[] address = ipv4Input.Split(".");
        validLength = address.Length == 4;
    };
    

    第一条规则规定 IPv4 地址需要有四个数字。 因此,在此代码中,使用 string.Split 分隔数字并检查其中包含四个数字。

  4. 更新 ValidateZeroes 方法,如下所示:

    void ValidateZeroes() 
    {
        string[] address = ipv4Input.Split(".");
    
        foreach (string number in address) 
        {
            if (number.Length > 1 && number.StartsWith("0")) 
            {
                validZeroes = false;
            }
        }
    
        validZeroes = true;
    }
    

    花点时间思考如何将规则转换为代码。

    第二条规则规定 IPv4 地址中的数字不得包含前导零。 因此,该方法需要检查数字中的前导零,同时接受 0 作为有效数字。 如果所有数字都具有有效的零,则 validZeroes 应等于 true,否则应等于 false。 因此,在此代码中,检查每个数位大于一的数字未以 0 开头。

    如果仔细观察,你会发现在 validZeroes 循环完成后,true 设置为 foreach。 但如果未找到前导零,只需将 validZeroes 设置为 true。 可通过在 validZeroes = true 循环运行之前设置 foreach 来更正此 bug。 但是,也可使用 return 语句更正此 bug。

  5. 将代码更新为以下内容:

    foreach (string number in address) 
    {
        if (number.Length > 1 && number.StartsWith("0")) 
        {
            validZeroes = false;
            return;
        }
    }
    

    return 语句终止方法的执行,并将控制返回给方法调用方。 如果在 return 之后添加 validZeroes = false 语句,则会在找到第一个无效的零后终止该方法。 如果未找到无效的零,该方法将在 validZeroes 设置为 true 后终止。 我们再来看看下一个方法。

  6. 更新 ValidateRange 方法,如下所示:

    void ValidateRange() 
    {
        string[] address = ipv4Input.Split(".");
    
        foreach (string number in address) 
        {
            int value = int.Parse(number);
            if (value < 0 || value > 255) 
            {
                validRange = false;
                return;
            }
        }
        validRange = true;
    }
    

    第三条规则规定 IPv4 地址中每个数字的范围必须在 0 到 255 之间。 因此,在此代码中,检查每个数字是否小于 255,如果不是,则在将 validRange 设置为 false 后终止执行。 由于输入字符串仅包含数字和点,因此无需检查负数。

    但可能存在点之间没有数字的情况。 例如“255...255”。 在这种情况下,string.Split(".") 将返回空条目,从而导致 int.Parse 失败。 可通过指定 StringSplitOptions 来防止这种情况。

  7. 按以下方式更新您的代码:

    string[] address = ipv4Input.Split(".", StringSplitOptions.RemoveEmptyEntries);
    

    使用 StringSplitOptions.RemoveEmptyEntries 时,会省略 address 数组中的空条目并防止尝试分析空字符串。

完成解决方案

所有方法都完成 IP 地址的验证后,便可重新访问初始解决方案。 在此任务中,你将添加更多输入值并准备测试代码。

  1. 找到前面在程序中编写的以下代码:

    string ipv4Input = "107.31.1.5";
    
  2. 按如下所示更新代码:

    string[] ipv4Input = {"107.31.1.5", "255.0.0.255", "555..0.555", "255...255"};
    

    开发解决方案时,请务必使用不同的输入用例测试代码。 在此代码中,提供一个合适的测试值范围。 更新测试输入后,需要更新代码才能使用新值。 由于值位于数组中,因此需要更新代码才能使用循环测试每个值。

  3. 按以下方式更新您的代码:

    foreach (string ip in ipv4Input) 
    {
        ValidateLength(); 
        ValidateZeroes(); 
        ValidateRange();
    
        if (validLength && validZeroes && validRange) 
        {
            Console.WriteLine($"{ip} is a valid IPv4 address");
        } 
        else 
        {
            Console.WriteLine($"{ip} is an invalid IPv4 address");
        }
    }
    

    最后,需要修复每个方法使用的输入数据,因为 ipv4Input 从字符串更新为了数组。 由于每个方法都使用 string.Split,因此可以声明一个变量来存储 string.Split 的结果并在每个方法中改为使用它。

  4. 添加变量以存储每个方法将引用的当前 IPv4 地址:

    string[] ipv4Input = {"107.31.1.5", "255.0.0.255", "555..0.555", "255...255"};
    string[] address;
    bool validLength = false;
    bool validZeroes = false;
    bool validRange = false;
    
  5. 使用 address 初始化 string.Split,如下所示:

    foreach (string ip in ipv4Input) 
    {
        address = ip.Split(".", StringSplitOptions.RemoveEmptyEntries);
    
  6. 从每个验证方法中移除对 string.Split 的引用,以便它们改用全局 address 变量。 例如:

    void ValidateLength() 
    {
        validLength = address.Length == 4;
    };
    

检查你的工作

在此任务中,从集成终端运行应用程序,并验证代码是否正常工作。 现在就开始吧。

  1. 将代码与以下内容进行比较,确保其正确:

    string[] ipv4Input = {"107.31.1.5", "255.0.0.255", "555..0.555", "255...255"};
    string[] address;
    bool validLength = false;
    bool validZeroes = false;
    bool validRange = false;
    
    foreach (string ip in ipv4Input) 
    {
        address = ip.Split(".", StringSplitOptions.RemoveEmptyEntries);
    
        ValidateLength(); 
        ValidateZeroes(); 
        ValidateRange();
    
        if (validLength && validZeroes && validRange) 
        {
            Console.WriteLine($"{ip} is a valid IPv4 address");
        } 
        else 
        {
            Console.WriteLine($"{ip} is an invalid IPv4 address");
        }
    }
    
    void ValidateLength() 
    {
        validLength = address.Length == 4;
    };
    
    void ValidateZeroes() 
    {
        foreach (string number in address) 
        {
            if (number.Length > 1 && number.StartsWith("0")) 
            {
                validZeroes = false;
                return;
            }
        }
    
        validZeroes = true;
    }
    
    void ValidateRange() 
    {
        foreach (string number in address) 
        {
            int value = int.Parse(number);
            if (value < 0 || value > 255) 
            {
                validRange = false;
                return;
            }
        }
        validRange = true;
    }
    
  2. 使用 Ctrl + S 或使用 Visual Studio Code 的“文件”菜单保存工作。

  3. 如有必要,打开 Visual Studio Code 的集成终端面板。

    在“资源管理器”面板中,若要在 TestProject 文件夹位置打开终端,请右键单击“TestProject”,然后选择“在集成终端中打开”。

  4. 在终端命令提示符处,输入 dotnet run

  5. 验证代码是否生成以下输出:

    107.31.1.5 is a valid IPv4 address
    255.0.0.255 is a valid IPv4 address
    555..0.555 is an invalid IPv4 address
    255...255 is an invalid IPv4 address
    

    如果代码显示不同的结果,则需要查看代码以查找错误并进行更新。 再次运行代码以查看是否已解决问题。 继续更新和运行代码,直到代码生成预期结果。

回顾

下面是到目前为止你了解到的关于使用方法的内容:

  • 方法可用于快速构建应用程序
  • return 关键字可用于终止方法执行
  • 问题的每个步骤通常都可以转换为它自己的方法
  • 使用方法解决小问题,以构建解决方案