【问题标题】:How do I create and handle multiple exceptions in Windows Forms?如何在 Windows 窗体中创建和处理多个异常?
【发布时间】:2019-04-01 18:00:53
【问题描述】:

在下面的代码中,我想处理 5 个可能由用户输入创建的不同异常。

我知道我应该使用IF 语句来处理这些异常,但要求 是使用异常处理程序来处理错误。所以请我只是在寻找这样做的输入,而不是替代方案。
我想用异常处理程序来处理它们。

我遇到的问题是在哪里放置异常处理代码。
另外,我有 5 个要检查的异常是否意味着我需要 5 个不同的 try/catch 块,或者我可以在同一个块中处理它们吗?

我正在寻找的例外是,尝试创建超过 19 个帐户,尝试创建初始余额低于 $300 的帐户,从帐户中提取超过当前余额,尝试在帐户上进行交易尚未创建并在TextBox 中输入除数字以外的任何内容。

因此,如果用户犯了这些错误之一,我想抛出错误并向用户显示他们所犯错误的消息。
非常感谢任何帮助。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MoreRobustBankGUI
{       
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private int _nextIndex = 0;
        List<Account> accounts = new List<Account>();
        decimal balance = 0;
        private void createButton1_Click(object sender, EventArgs e)
        {
            if (accounts.Count < 19 && balance > 300)
            {
                _nextIndex++;
                int accountId = _nextIndex;
                decimal.TryParse(amountTextBox2.Text, out balance);

                transactionLabel3.Text = "Account: #" + accountId + " created with a starting balance of $" + balance;
                accountTextBox1.Text = "" + accountId;

                accounts.Add(new Account(balance)
                {
                    AccountId = accountId
                });
            }
            else
            {
                transactionLabel3.Text = "Can only create up to 19 accounts and starting balance must be $300";
            }
        }

        private void executeButton2_Click(object sender, EventArgs e)
        {
            decimal amount = 0;
            int accountID;

            string textAmount = amountTextBox2.Text == "" ? "0" : amountTextBox2.Text;

            if (depositRadioButton3.Checked == true)
            {
                if (string.IsNullOrEmpty(accountTextBox1.Text)) return;

                bool accountCanBeConverted = int.TryParse(accountTextBox1?.Text, out accountID);
                bool ammountCanBeConverted = decimal.TryParse(amountTextBox2?.Text, out amount);
                if (accountCanBeConverted && ammountCanBeConverted && amount > 0)
                {
                    var selectedAccount = GetAccount(accountID);
                    selectedAccount.DepositFunds(amount);
                    transactionLabel3.Text = $"Account: #{selectedAccount.AccountId} You made a deposit of ${amount}";
                }

            }
            else if (withdrawRadioButton2.Checked == true)
            {
                if (string.IsNullOrEmpty(accountTextBox1.Text)) return;

                bool accountCanBeConverted = int.TryParse(accountTextBox1?.Text, out accountID);
                bool ammountCanBeConverted = decimal.TryParse(amountTextBox2?.Text, out amount);
                if (accountCanBeConverted && ammountCanBeConverted && amount > 0)
                {
                    var selectedAccount = GetAccount(accountID);
                    if (selectedAccount.HasAvailableFunds)
                    {
                        selectedAccount.WithdrawFromAccount(amount);
                        transactionLabel3.Text = $"Account: #{selectedAccount.AccountId} You made a withdrawal of ${amount}";
                    }
                    else
                    {
                        transactionLabel3.Text = $"Account: #{selectedAccount.AccountId} Does not have available funds to withdraw";
                    }
                }
            }
            else if (balanceRadioButton3.Checked == true)
            {
                if (string.IsNullOrEmpty(accountTextBox1.Text)) return;

                bool accountCanBeConverted = int.TryParse(accountTextBox1?.Text, out accountID);

                var selectedAccount = GetAccount(accountID);
                var balance = selectedAccount.GetAvailableBalanceForAccount(accountID);

                if (balance == -1234567890)
                {
                    transactionLabel3.Text = $"Invalid account number passed.";
                }
                else
                {
                    transactionLabel3.Text = $"Account: #{selectedAccount.AccountId} Balance: $ {selectedAccount.GetAvailableBalanceForAccount(accountID)}";
                }
            }

            clearFields();
        }

        public void clearFields()
        {
            amountTextBox2.Text = "";
        }
        public Account GetAccount(int id)
        {
            return accounts.Where(x => x.AccountId == id).FirstOrDefault();
        }

        public class Account
        {

            public Account(decimal balance)
            {
               Balance = balance;
            }

            public int AccountId { get; set; }

            public decimal Balance { get; set; }

            public void WithdrawFromAccount(decimal deductionAmount)
            {
                Balance -= deductionAmount;
            }

            public void DepositFunds(decimal depositAmount)
            {
                Balance += depositAmount;
            }

            public bool HasAvailableFunds => Balance > 0;

            public decimal GetAvailableBalanceForAccount(int accountId)
            {
                if (accountId == AccountId)
                {
                    return Balance;
                }
                else
                {
                    return -1234567890;
                }
            }
        }
    }
}

【问题讨论】:

  • 您只能处理抛出的异常。您所指的这 5 个异常(ArgumentException、InvalidOperationException 等)的类型是什么,它们是在哪里抛出的?还是您可能是想问“我如何从/在我的代码中抛出自定义异常”?
  • 是的,我在帖子中提到了例外情况,但他们又来了。我正在寻找的例外情况是,尝试创建一个初始余额低于 300 美元的帐户,尝试创建超过 19 个帐户,从帐户中提取超过当前余额,尝试在尚未创建的帐户上进行交易并在文本框中输入除数字以外的任何内容。所以是的,我需要尝试一下,然后在创建时抛出错误,最后向用户发送一条消息,说明他们犯了什么错误。
  • 您可能希望在这里了解更多关于如何处理(捕获)异常以及如何创建用户定义的异常的信息:docs.microsoft.com/en-us/dotnet/standard/exceptions 这有望为您提供足够的知识来实现​​您的目标.
  • 好的,谢谢。我会看看它是否对我有帮助。

标签: c# winforms error-handling


【解决方案1】:

对不起,这是一个“不要那样做”的答案......

对“正常”业务控制流使用异常不是好的做法。例外应该是针对特殊事件。

人们创建余额太少的帐户显然是正常的,并且(无论如何对我而言)尝试提取的金额超过帐户中的余额是很正常的。这些错误应该由正常的控制流处理((if balance &lt; MIN_BALANCE) 类型的东西)。

更多讨论请看这里https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why

对于前进的方向,可能会调查在业务规则被破坏时引发事件。 有很多方法可以做到这一点......这是一个你可以尝试的简单方法。 Understanding events and event handlers in C#

【讨论】:

  • 是的,我完全理解这一点,我同意,但正如我在帖子中所说,要求是仅使用异常来处理问题,而不是 IF 语句,尽管不幸的是那将是更好的路线不是这里的要求。
【解决方案2】:

我同意这是一个糟糕的主意,希望你真的知道自己在做什么。正确的异常处理是我的烦恼,你的想法听起来并不可靠。以下是我认为必须阅读和链接的两篇文章:

https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/

https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET

话虽如此,有人曾经遇到过无法使用 TryParse 的问题,因为他在 .NET 1.1 上运行。所以我很快将这个 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;

}

我认为问题(非常远的异常处理完全相同)与您的问题相同。

【讨论】:

  • 是的,我也同意 Loofer 的观点,并且已经知道使用 IF 语句来处理用户错误,正如您在我的代码中看到的那样我通常不会采用这种方法的唯一原因。感谢您的意见,我会看看您提供的内容。
【解决方案3】:

虽然我完全同意@Loofer 的回答(+1)。

看来你有不同的用例。

所以给出答案

另外,我有 5 个我想检查的异常是否意味着我 需要 5 个不同的 try/catch 块,或者我可以同时处理它们吗 屏蔽?

你应该使用 Multiple Catch 块

类似的东西

try
{
    //
}
catch(Type1Exception exception)
{
}
catch(Type2Exception exception)
{
}
catch(Type3Exception exception)
{
}

等等。

但还有另一种方法可以回答您的两个问题。

这也是个人的建议,那就是创建一个类似的辅助方法

private void HandleCustomException(Exception exception)
{
    // Your Error Handling Code goes here
    if(exception is Type1Exception)
    {...}
    ...
}

然后在你的点击事件中加入单独的try catch,这会将收到的任何异常发送到这个辅助方法

类似的东西

private void createButton1_Click(object sender, EventArgs e)
{
    try
    {
        if(Your Condition)
        {
            throw new Type1Exception();
        }
    }
    catch(Exception exception)
    {
        HandleCustomException(exception);
    }
}

【讨论】:

  • 是的,我也同意 Loofer 的观点,并且已经知道使用 IF 语句来处理用户错误,正如您在我的代码中看到的那样我通常不会采用这种方法的唯一原因。谢谢你的回答,我会试试的。我想这可能会给我我想要的东西,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 2021-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多