提问者:小点点

实现while循环以检查输入有效性中的多个异常


抱歉,如果这是一个简单的问题; 这是我的第一语言,我正在尽我最大的努力寻找和遵循这个网站和其他的例子和解释。

我一直在尝试扩展一个创建“银行帐户”的Microsoft C#教程程序。 我正在尝试捕获和处理异常,特别是通过提示用户再次尝试获取有效的输入。

我遇到过这个线程和许多类似的线程,这些线程是关于在输入无效的情况下运行循环的,这个例子特别使用try/catch,如果我理解正确的话,它就是我想在这里使用的,因为我有几行代码可以抛出多个异常(它可以是非数值的,也可以是负值的)。 根据这些示例和其他示例,我不知道如何将初始余额输入分配给一个值,一旦输入有效,该值可以在循环之外引用(但仍然只能在CreateAccount方法内引用)。

我不确定当前的代码是否正常工作,但是当前这段代码会产生一个错误,因为在while循环之后,initBalInput没有被赋值,即使它是在循环之外声明的,并且是在try块中赋值的。

public static void CreateAccount()
        {


            // Prompt for BankAccount constructor parameter {name} which is passed to BankAccount.Owner in constructor
            Console.WriteLine("Name on new account: ");
            string nameInput = Console.ReadLine();

            decimal initBalInput;
            bool valid = false;
            while (valid == false)
            {
                try
                {
                    Console.WriteLine("How much to deposit for initial balance: ");
                    initBalInput = Convert.ToDecimal(Console.ReadLine());
                }
                catch (ArgumentOutOfRangeException)
                {
                    Console.WriteLine("Initial balance must be positive!");
                    valid = false;
                    continue;
                }
                catch (FormatException)
                {
                    Console.WriteLine("Initial balance must be a number!");
                    valid = false;
                    continue;
                }
                valid = true;
            }

            // Create new instance "account" of type BankAccount and set its parameters
            BankAccount account = new BankAccount(nameInput, initBalInput);
            Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");
        }

共3个答案

匿名用户

编写处理无效输入的代码,而不是捕获异常。

public static void CreateAccount()
        {


            // Prompt for BankAccount constructor parameter {name} which is passed to BankAccount.Owner in constructor
            Console.WriteLine("Name on new account: ");
            string nameInput = Console.ReadLine();

            string initBalInput = Console.ReadLine();
            // try parse will check for invalid decimal values and also, positive values can be checked
            if(decimal.TryParse(initBalInput, out decimal initBal) && initBal > 0) {

                // Create new instance "account" of type BankAccount and set its parameters
                BankAccount account = new BankAccount(nameInput, initBal);
                Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");


            } else {
             Console.WriteLine("Invalid initial balance");
          }
        }

匿名用户

首先,我有两篇我认为必须阅读的关于异常处理的文章:

  1. 这一项有助于对4种常见异常类型进行分类--如果您甚至应该考虑捕获它们的话。
  2. 而本篇将详细介绍良好实践。

您不应该使用convert,而应该使用parse。 或者更好的tryparse()。 字符串上的异常->; 数字转换是令人烦恼的例外的例子。

如果没有TryParse,我曾经为仍在研究框架1.1的人编写了Int.TryParse()的自定义实现:

//Parse throws ArgumentNull, Format and Overflow Exceptions.
//And they only have Exception as base class in common, but identical handling code (output = 0 and return false).

bool TryParse(string input, out int output){
  try{
    output = int.Parse(input);
  }
  catch (Exception ex){
    if(ex is ArgumentNullException ||
      ex is FormatException ||
      ex is OverflowException){
      //these are the exceptions I am looking for. I will do my thing.
      output = 0;
      return false;
    }
    else{
      //Not the exceptions I expect. Best to just let them go on their way.
      throw;
    }
  }

  //I am pretty sure the Exception replaces the return value in exception case. 
  //So this one will only be returned without any Exceptions, expected or unexpected
  return true;
}

但该代码看起来像是您希望了解失败原因的详细信息。 在这一点上,您可能必须编写一个详细的catch块列表。

匿名用户

但是当前,此代码会产生错误,因为在while循环之后,initBalInput未被赋值,即使它是在循环之外声明的,并且是在try块中赋值的

问题是编译器不知道执行是否会到达try块:

while (valid == false)

在运行时计算。 您和我都知道,执行将至少进入一次while循环,因为valid最初是false,但编译器不会进入涉及变量的那种类型的分析,因此假定执行可能永远不会进入while循环,并且可以读取一个单元化的initbalinput

也就是说,您不应该养成使用Exeptions作为控制流机制的习惯。 异常应该是异常,不要把程序的逻辑建立在异常的基础上。 在您的示例中,应该研究Decimal.TryParse方法。

还有,总是把你的问题分解成更小的问题。 一开始,从小处着手,做一个明显正确的线性方法。 在只有一两行长的方法中编写bug是非常困难的。

那你需要什么?

  1. 提示用户输入的方法。
  2. 验证输入的方法
  3. 如果输入错误,要求用户重试的操作。

好吧,第一点:

static string RequestUserInput(string message)
{
    Console.Write(message);
    return Console.ReadLine();
}

第二个:我们已经用decimal.tryparse(string,out decimal d)提供了它。 如果输入字符串可以解析为有效的十进制数,则此方法将返回true,否则将分配给dfalse

第三:

public static decimal GetDecimalInput(string message)
{
    decimal d;

    while (true)
    {
        if (!decimal.TryParse(RequestUser(message, out d))
            //tryparse failed, input is not a valid decimal number
            Console.WriteLine("Initial balance must be a number!");
        else if (d < 0) //try parse succeeded, we know input is a valid
                        // decimal number d but it might be negative.
            Console.WriteLine("Initial balance must be positive!");
        else
            //we know inout is a valid non negative decimal number.
            //break out of the loop, we don't need to ask again.
            break;
    }

    return d;
}

现在,你把它们放在一起:

var accountBalance = GetDecimalInput("How much to deposit for initial balance: ");