0

In the code below, which does work, I would like to handle 5 different exceptions that could be created by user input.

I understand I should use an IF statement to handle these exceptions but the requirement is to handle the errors with exception handlers. So please I am only looking for input in doing that and not alternatives.
I would like to handle them with exception handlers.

The problem I am having is where to put the exception handling code.
Also being I have 5 exceptions I want to check for does that mean I need 5 different try/catch blocks or can I handle them all in the same block?

The exceptions I am looking for are, trying to create more than 19 accounts, trying to create an account with an initial balance below $300, withdrawing more than the current balance from an account, attempting a transaction on an account that hasn't been created and entering anything other than a number in the TextBox.

So if a user makes one of these errors I would like to throw the error and display a message to the user of the error they have made.
Any assistance is greatly appreciated.

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;
                }
            }
        }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
Demond
  • 109
  • 5
  • You can only handle exceptions that are **thrown**. What is the type of these 5 exceptions (ArgumentException, InvalidOperationException, etc...) you are referring to and where are those thrown? Or did you perhaps mean to ask "how do i throw custom exceptions from/in my code"? –  Oct 28 '18 at 18:56
  • Yes I mentioned the exceptions in the post but here they are again. The exceptions I am looking for are, trying to create an account with an initial balance below $300, trying to create more than 19 accounts, withdrawing more than the current balance from an account, attempting a transaction on an account that hasn't been created and entering anything other than a number in the textbox. So yes I need to put them in a try, then throw the error if created and finally send a message to the user as to what the error is they made. – Demond Oct 28 '18 at 19:00
  • You might want to look here to learn more about both how to handle (catch) exceptions and how to create user-defined exceptions: https://learn.microsoft.com/en-us/dotnet/standard/exceptions/ This should hopefully equip you with enough knowledge to achieve your goal. –  Oct 28 '18 at 19:01
  • Ok thanks. i will take a look and see if it will help me with this. – Demond Oct 28 '18 at 19:03

3 Answers3

3

Sorry for this being a 'don't do that' answer...

Using exceptions for 'normal' business control flow is not good practice. Exceptions should be for exceptional events.

It is clearly normal for people to create accounts with too little balance, and (for me anyway) to try and withdraw more than they have in the account. These errors should be handled in by normal control flows ( (if balance < MIN_BALANCE) type things).

For a bigger discussion look here https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why

For a way forward maybe investigate raising events when business rules are broken. There are a lot of ways you can do this... here is a simple thing you could try. Understanding events and event handlers in C#

Loofer
  • 6,841
  • 9
  • 61
  • 102
  • yes i totally understand that and I agree, But as I stated in the post the requirement is to use exceptions ONLY to handle the problem and not an IF statement although that would be the better route unfortunately that is not the requirement here. – Demond Oct 28 '18 at 18:47
1

I agree that is a poor idea and hope you really know what you are doing. Proper exception handling is a pet peeve of mine and your idea does not sound remotely solid. Here are two articles on the matter I consider must reads and link a lot:

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

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

That all being said, somebody once had a problem that he could not use TryParse because he was running on .NET 1.1. So I quickly cobelled this tryParse alternative together:

//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;

}

I think the problem (very far apart exceptions with the exact same handling) is the same as yours.

Loofer
  • 6,841
  • 9
  • 61
  • 102
Christopher
  • 9,634
  • 2
  • 17
  • 31
  • Yes I agree with Loofer also and was already aware of using IF statements to handle user error as you saw in my code I was doing that but unfortunately the requirement asked that I use exception handlers instead so that is the only reason why I am taking this approach as I normally would not. Thank you for your input and I will take a look at what you have provided. – Demond Oct 28 '18 at 19:24
1

Although i totally agree with @Loofer's answer (+1 for that).

Seems you have different Use case.

So giving answer to that

Also being I have 5 exceptions I want to check for does that mean I need 5 different try/catch blocks or can I handle them all in the same block?

You should use Multiple Catch block

Something Like

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

And So On.

But there is another way also which answers both of your question.

which is personal suggestion too and that would be to create one helper method something like

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

And then put separate try catch in you click events, which will send any Exception received to this helper method

Something like this

private void createButton1_Click(object sender, EventArgs e)
{
    try
    {
        if(Your Condition)
        {
            throw new Type1Exception();
        }
    }
    catch(Exception exception)
    {
        HandleCustomException(exception);
    }
}
Mihir Dave
  • 3,954
  • 1
  • 12
  • 28
  • Yes I agree with Loofer also and was already aware of using IF statements to handle user error as you saw in my code I was doing that but unfortunately the requirement asked that I use exception handlers instead so that is the only reason why I am taking this approach as I normally would not. Thank you for your answer and I will try it. I think this may give me what I am looking for, Thanks. – Demond Oct 28 '18 at 19:22