Handling many Exceptions

Catching Various Exceptions

In previous examples (in the previous lesson), when we anticipated some type of problem, we instructed the compiler to use our default catch section. We left it up to the compiler to find out when there was a problem and we provided a catch section to deal with it. A section of code with numerous or complex operations and requests can also produce different types of errors. You should be able to face different problems and deal with them individually, each by its own kind. To do this, you can create different catch sections, each made for a particular error. The formula to follow is:

try {
	// Code to Try
}
catch(arg1)
{
	// One Exception
}
catch(arg2)
{
	// Another Exception
}

The compiler would proceed from top-down:

  1. Following the normal flow of the program, the compiler enters the try block
  2. If no exception occurs in the try block, the rest of the try block executes.
    If an exception occurs in the try block, the compiler registers the type of error that occurred. If there is a throw line, the compiler registers it also:
    1. The compiler gets out of the try section
    2. The compiler examines the first catch section. If the first catch clause matches the thrown error, that catch section executes and the exception handling routine may end. If the first catch doesn't match the thrown error, the compiler proceeds with the next catch clause
    3. The compiler checks the next match, if any, and proceeds as in the first catch. This continues until the compiler finds a catch clause that matches the thrown error
    4. If one of the catch clauses matches the thrown error, its body executes. If no catch clause matches the thrown error, the compiler calls the Exception class and uses the default message

Multiple catch clauses are written if or when a try block is expected to throw different types of errors.

Practical LearningPractical Learning: Introducing Exception Handling

  1. Start Microsoft Visual Studio. Create a new Console App named PayrollPreparation6 that uses the .NET 8.0 (Long-Term Support)
  2. In the Solution Explorer, right-click -> Add -> New Folder
  3. Type Models as the name of the new folder
  4. In the Solution Explorer, right-click the new Models folder -> Add -> Class...
  5. Type Employee as the name of the new class/file
  6. Click Add
  7. Change the class as follows:
    namespace PayrollPreparation6.Models
    {
        internal class Employee
        {
            internal int     EmployeeNumber { get; set; }
            internal string? FirstName      { get; set; }
            internal string? LastName       { get; set; }
            internal double  HourlySalary   { get; set; }
        }
    }
  8. In the Solution Explorer, right-click Models -> Add -> Class...
  9. Type TimeSheet as the name of the new class/file
  10. Click Add
  11. Change the class as follows:
    namespace PayrollPreparation6.Models
    {
        internal class TimeSheet
        {
            private int    tsNbr;
            private int    empl;
            private double salary;
            private double result;
    
            private double mon, tue, wed, thu, fri;
    
            internal TimeSheet(int ts, int emplNbr, double m, double t, double w, double h, double f)
            {
                mon   = m;
                tue   = t;
                wed   = w;
                thu   = h;
                fri   = f;
                tsNbr = ts;
                empl  = emplNbr;
            }
    
            internal ref int    GetTimeSheetNumber()  => ref tsNbr;
            internal ref int    GetEmployeeNumber()   => ref empl;
            internal ref double GetMondayWork()       => ref mon;
            internal ref double GetTuesdayWork()      => ref tue;
            internal ref double GetWednesdayWork()    => ref wed;
            internal ref double GetThursdayWork()     => ref thu;
            internal ref double GetFridayWork()       => ref fri;
    
            private ref double GetSalary()
            {
                salary = 0.00;
    
                if (empl == 370_595)
                {
                    salary = 28.25;
                }
                else if (empl == 395_796)
                {
                    salary = 18.86;
                }
                else if (empl == 826_384)
                {
                    salary = 24.37;
                }
                else if (empl == 175_004)
                {
                    salary = 26.97;
                }
                else if (empl == 697_415)
                {
                    salary = 31.57;
                }
                else
                    salary = 0.00;
    
                return ref salary;
            }
    
            internal double CalculateTimeWorked() => mon + tue + wed + thu + fri;
    
            internal ref double CalculateRegularTime(ref readonly double time)
            {
                result = time;
    
                if (time is > 40.00)
                {
                    result = 40.00;
                }
    
                return ref result;
            }
    
            internal ref double CalculateRegularPay(ref readonly double time)
            {
                result = GetSalary() * time;
    
                if (time is > 40.00)
                {
                    result = GetSalary() * 40.00;
                }
    
                return ref result;
            }
    
            internal ref double CalculateOvertime(ref readonly double time)
            {
                result = 0.00;
    
                if (time is > 40.00)
                {
                    result = time - 40.00;
                }
    
                return ref result;
            }
    
            internal ref double CalculateOvertimePay(ref readonly double time)
            {
                result = 0.00;
    
                if (time is > 40.00)
                {
                    result = salary * 1.50 * time;
                }
    
                return ref result;
            }
    
            internal double CalculateGrossPay(ref double time) => CalculateRegularPay(ref time) + CalculateOvertimePay(ref time);
        }
    }
  12. In the Solution Explorer, right-click Program and click Rename
  13. Type PayrollProcessing (to get PayrollProcessing.cs)
  14. Above the Code Editor, click the PayrollProcessing.cs tab and change the document as follows:
    using PayrollPreparation6.Models;
    using static System.Console;
    
    PreparePayroll();
    
    Employee Hire(in int number)
    {
        Employee empl1 = new Employee()
        {
            EmployeeNumber = 370_595,
            FirstName = "Michael",
            LastName = "Carlock",
            HourlySalary = 28.25
        };
        Employee empl2 = new Employee()
        {
            EmployeeNumber = 826_384,
            FirstName = "Catherine",
            LastName = "Busbey",
            HourlySalary = 24.37
        };
        Employee empl3 = new Employee()
        {
            EmployeeNumber = 175_004,
            FirstName = "Andrew",
            LastName = "Sanders",
            HourlySalary = 26.97
        };
        Employee empl4 = new Employee()
        {
            EmployeeNumber = 697_415,
            FirstName = "Jennifer",
            LastName = "Simms",
            HourlySalary = 31.57
        };
    
        Employee empl5 = new Employee()
        {
            EmployeeNumber = 395_796,
            FirstName = "Richard",
            LastName = "Stones",
            HourlySalary = 18.86
        };
    
        if (number == 370_595)
        {
            return empl1;
        }
        else if (number == 826_384)
        {
            return empl2;
        }
        else if (number == 175_004)
        {
            return empl3;
        }
        else if (number == 697_415)
        {
            return empl4;
        }
        else if (number == 395_796)
        {
            return empl5;
        }
    
        return new Employee();
    }
    
    TimeSheet GetTimeWorked(in int number)
    {
        TimeSheet ts1 = new TimeSheet(100_000, 370_595, 7, 8, 6.5, 8.5, 6.5);
        TimeSheet ts2 = new TimeSheet(205_000, 826_384, 9.5, 8, 10.5, 9, 8.5);
        TimeSheet ts3 = new TimeSheet(505_500, 175_004, 9, 10.5, 7, 9.5, 8.5);
        TimeSheet ts4 = new TimeSheet(202_240, 697_415, 8, 8, 8, 8, 8);
        TimeSheet ts5 = new TimeSheet(693_748, 395_796, 8, 6.5, 7, 9, 6);
    
        if (number == 205_000)
        {
            return ts2;
        }
        else if (number == 202_240)
        {
            return ts4;
        }
        else if (number == 505_500)
        {
            return ts3;
        }
        else if (number == 693_748)
        {
            return ts5;
        }
        else if (number == 100_000)
        {
            return ts1;
        }
    
        return new TimeSheet(0, 0, 0, 0, 0, 0, 0);
    }
    
    void PreparePayroll()
    {
        try
        {
            Write("Enter the Id of the time sheet to view: ");
            int nbr = int.Parse(ReadLine()!);
    
            TimeSheet timeSheet = GetTimeWorked(nbr);
            int emplNumber = timeSheet.GetEmployeeNumber();
    
            Employee staff = Hire(emplNumber);
    
            WriteLine("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+");
            WriteLine("FUN DEPARTMENT STORE");
            WriteLine("=======================================================");
            WriteLine("Payroll Evaluation");
            WriteLine("=======================================================");
            ShowEmployee(ref staff);
            PresentPayroll(ref timeSheet);
        }
        catch(FormatException fe)
        {
            WriteLine("--------------------------------------------------------------");
            WriteLine("The number you provided for the time sheet to view is not valid.");
            WriteLine("The error produced is: " + fe.Message);
            WriteLine("==============================================================");
        }
        catch (Exception exc)
        {
            WriteLine("--------------------------------------------------------------");
            WriteLine("There was an error in your application. The error produced is:");
            WriteLine(exc.Message);
            WriteLine("==============================================================");
        }
    }
    
    void ShowEmployee(ref readonly Employee empl)
    {
        WriteLine("Employee Information");
        WriteLine("-------------------------------------------------------");
        WriteLine("Employee #:      {0}", empl.EmployeeNumber);
        WriteLine($"Full Name:       {empl.FirstName} {empl.LastName}");
        WriteLine($"Hourly Salary:   {empl.HourlySalary:f}");
        WriteLine("=======================================================");
    }
    
    void PresentPayroll(ref readonly TimeSheet ts)
    {
        double timeWorked = ts.CalculateTimeWorked();
    
        WriteLine("Time Sheet #:    {0}", ts.GetTimeSheetNumber());
        WriteLine("-------------------------------------------------------");
        WriteLine("Time Worked Summary");
        WriteLine("--------+---------+-----------+----------+-------------");
        WriteLine(" Monday | Tuesday | Wednesday | Thursday | Friday");
        WriteLine("--------+---------+-----------+----------+-------------");
        Write($"  {ts.GetMondayWork():f}  |   ");
        Write($"{ts.GetTuesdayWork():f}  |    ");
        Write($"{ts.GetWednesdayWork():f}   |   ");
        Write($"{ts.GetThursdayWork():f}   |  ");
        WriteLine($"{ts.GetFridayWork():f}");
        WriteLine("========+=========+===========+==========+=============");
        WriteLine("                                  Pay Summary");
        WriteLine("-------------------------------------------------------");
        WriteLine("                                  Time   Pay");
        WriteLine("-------------------------------------------------------");
        WriteLine("                     Regular:    {0:f}   {1:f}",
                ts.CalculateRegularTime(ref timeWorked),
                ts.CalculateRegularPay(ref timeWorked));
        WriteLine("-------------------------------------------------------");
        WriteLine("                     Overtime:    {0:f}   {1:f}",
                ts.CalculateOvertime(ref timeWorked),
                ts.CalculateOvertimePay(ref timeWorked));
        WriteLine("=======================================================");
        WriteLine("                     Net Pay:            {0:f}",
                ts.CalculateGrossPay(ref timeWorked));
        WriteLine("=======================================================");
    }
  15. To execute, on the main menu, click Debug -> Start Without Debugging
  16. When requested, type the number as "I don' remember" and press Enter
    Enter the Id of the time sheet to view: I don't remember
    --------------------------------------------------------------
    The number you provided for the time sheet to view is not valid.
    The error produced is: The input string 'I don't remember' was not in a correct format.
    ==============================================================
    
    Press any key to close this window . . .
  17. Return to your programming environment

Filtering an Exception

Remember that a section of your code could end up throwing various exceptions or different types of exceptions. We saw that, to address such a situation, you can create a catch clause for each possibility. As an alternative, the C# language provides a keyword named when. The formula to use it is:

try
{
	. . .
}
catch(exception-type) when (condition)
{
	. . .
}

Once again, the compiler will first deal with the code in the try section. If there is a problem, then the compiler would move to the catch section. In that section, pass an exception-type, which is usually an Exception parameter or a class derived from Exception.

Filtering exceptions consists of specifying a criterion by which a particular exception, among others, would be selected. To proceed, after the closing parenthesis of the catch keyword, type the when (contextual) keyword. This keyword functions like an if conditional statement. This means that when must be followed by parentheses. In the parentheses of when(), create a logical expression (an expression that can evaluate to true or false). If that expression is true, in the body of the catch clause, create a consequential expression. This means that the body of the catch clause would execute if the when() conditional statement is true.

ApplicationPractical Learning: Filtering an Exceptions

  1. Change the document as follows:
    using PayrollPreparation6.Models;
    using static System.Console;
    
    PreparePayroll();
    
    Employee Hire(in int number)
    {
        . . .
    
        return new Employee();
    }
    
    TimeSheet GetTimeWorked(in int number)
    {
        . . .
    
        return new TimeSheet(0, 0, 0, 0, 0, 0, 0);
    }
    
    void PreparePayroll()
    {
        try
        {
            Write("Enter the Id of the time sheet to view: ");
            int nbr = int.Parse(ReadLine()!);
    
            TimeSheet timeSheet = GetTimeWorked(nbr);
            int emplNumber = timeSheet.GetEmployeeNumber();
    
            Employee staff = Hire(emplNumber);
    
            WriteLine("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+");
            WriteLine("FUN DEPARTMENT STORE");
            WriteLine("=======================================================");
            WriteLine("Payroll Evaluation");
            WriteLine("=======================================================");
            ShowEmployee(ref staff);
            PresentPayroll(ref timeSheet);
        }
        catch (Exception exc) when (exc is FormatException fe)
        {
            WriteLine("--------------------------------------------------------------");
            WriteLine("The number you provided for the time sheet to view is not valid.");
            WriteLine("The error produced is: " + fe.Message);
            WriteLine("==============================================================");
        }
    }
    
    void ShowEmployee(ref readonly Employee empl)
    {
        . . .
    }
    
    void PresentPayroll(ref readonly TimeSheet ts)
    {
        . . .
    }
  2. To execute, on the main menu, click Debug -> Start Without Debugging
  3. When requested, type the number as "I don' know" and press Enter
    Enter the Id of the time sheet to view: I don't know
    --------------------------------------------------------------
    The number you provided for the time sheet to view is not valid.
    The error produced is: The input string 'I don't know' was not in a correct format.
    ==============================================================
    
    Press any key to close this window . . .
  4. Press E to close the window and return to your programming environment
  5. To open a previous project, on the main menu, click Project -> Recent Projects and Solutions and click the option for PledgeDistribution1

Filtering many Exceptions

Remember that a section of code can produce many errors. In other words, a section can produce various exceptions. As a result, you can create various catch sections, each with its when condition.

ApplicationPractical Learning: Filtering many Exceptions

  1. Change the document as follows:
    using static System.Console;
    
    (int, int) DistributeParts(int amount, int portion1, int portion2)
    {
        int totalRatios = portion1 + portion2;
        int eachPart = amount / totalRatios;
        int result = eachPart * portion1;
    
        return (result, amount - result);
    }
    
    int Subtract(int first, int second)
    {
        return first - second;
    }
    
    try
    {
        int amtPledged;
        int ratio1;
        int ratio2;
    
        WriteLine("===================================================");
        WriteLine("Pledge Distribution");
        WriteLine("===================================================");
        WriteLine("Type the values to evaluate a pledge distribution");
        Write("Amount to Allocate:  ");
        amtPledged = int.Parse(ReadLine()!);
        Write("Portion 1:           ");
        ratio1 = int.Parse(ReadLine()!);
        Write("Portion 2:           ");
        ratio2 = int.Parse(ReadLine()!);
    
        (int first, int second) portions = DistributeParts(amtPledged, ratio1, ratio2);
    
        WriteLine("===================================================");
        WriteLine("Pledge Distribution");
        WriteLine("===================================================");
        WriteLine($"Amouont Allocated:   {amtPledged}");
        WriteLine($"Portion 1:           {ratio1}");
        WriteLine($"Portion 2:           {ratio2}");
        WriteLine("---------------------------------------------------");
        WriteLine($"Part 1:              {portions.first}");
        WriteLine($"Part 2:              {portions.second}");
        WriteLine("===================================================");
    }
    catch(Exception exc) when ( exc is FormatException fe)
    {
        WriteLine("---------------------------------------------------------------");
        WriteLine("The value you entered for the amount to pledge " +
                  "(or to allocate) is not valid. ");
        WriteLine("Please type a valid value (such as 200, 1250, 5000, or 100000).");
        WriteLine("==============================================================");
    
        WriteLine("The error produced is: " + fe.Message);
    }
    catch (Exception exc) when (exc is DivideByZeroException dze)
    {
        WriteLine("---------------------------------------------------------------");
        WriteLine("It appears that you entered 0 for the ratio(s), which is not allowed.");
        WriteLine("The error produced is: " + dze.Message);
        WriteLine("==============================================================");
    }
  2. To execute, on the main menu, click Debug -> Start Without Debugging
  3. When requested, type the amount to allocate as "I pledge 10,000 dollars" and press Enter
    ===================================================
    Pledge Distribution
    ===================================================
    Type the values to evaluate a pledge distribution
    Amount to Allocate:  I pledge 10,000 dollars
    ---------------------------------------------------------------
    The value you entered for the amount to pledge (or to allocate) is not valid.
    Please type a valid value (such as 200, 1250, 5000, or 100000).
    ==============================================================
    The error produced is: The input string 'I pledge 10,000 dollars' was not in a correct format.
    
    Press any key to close this window . . .
  4. Return to your programming environment
  5. To execute, on the main menu, click Debug -> Start Without Debugging
  6. Type the amount to allocate as 10000 and press Enter
  7. Type Portion 1 as Six and press Enter:
    ===================================================
    Pledge Distribution
    ===================================================
    Type the values to evaluate a pledge distribution
    Amount to Allocate:  10000
    Portion 1:           Six
    ---------------------------------------------------------------
    The value you entered for the amount to pledge (or to allocate) is not valid.
    Please type a valid value (such as 200, 1250, 5000, or 100000).
    ==============================================================
    The error produced is: The input string 'Six' was not in a correct format.
    
    Press any key to close this window . . .
  8. Return to your programming environment
  9. To execute, on the main menu, click Debug -> Start Without Debugging
  10. Type the amount to allocate as 10000 and press Enter
  11. Type Portion 1 as 0 and press Enter
  12. Type Portion 2 as 0 and press Enter:
    ===================================================
    Pledge Distribution
    ===================================================
    Type the values to evaluate a pledge distribution
    Amount to Allocate:  10000
    Portion 1:           0
    Portion 2:           0
    ---------------------------------------------------------------
    It appears that you entered 0 for the ratio(s), which is not allowed.
    The error produced is: Attempted to divide by zero.
    ==============================================================
    
    Press any key to close this window . . .
  13. Return to your programming environment

Throwing an Exception

Introduction

As mentioned previously, the Exception class is equipped with a Message property that carries a message for the error that occurred. We also mentioned that the message of this property may not be particularly useful to a user. Fortunately, you can create your own message and pass it to an Exception object. To be able to receive custom messages, the Exception class provides the following constructor:

public Exception(string message);

As mentioned already, all exceptions classes derive directly or indirectly from Exception. As a result, all those classes are equipped with a default constructor and all of them inherit the constructor that takes a string as argument.

To use the constructor that uses a string parameter, in the section in which you are anticipating the error, use a keyword named throw. Follow this keyword by the new operator to instantiate the Exception class using this constructor. Here is an example:

using static System.Console;

try
{
    double result = 0.00;
    string oper = "";
    double number1 = 0.00, number2 = 0.00;

    Write("Number 1: ");
    number1 = double.Parse(ReadLine()!);
    Write("Operator: ");
    oper = ReadLine()!;
    Write("Number 2: ");
    number2 = double.Parse(ReadLine()!);

    if ((oper != "+") && (oper != "-") && (oper != "*") && (oper != "/"))
    {
        throw new Exception(oper);
    }

    switch (oper)
    {
        case "+":
            result = number1 + number2;
            break;
        case "-":
            result = number1 - number2;
            break;
        case "*":
            result = number1 * number2;
            break;
        case "/":
            result = number1 / number2;
            break;
        default:
            WriteLine("Bad Operation");
            break;
    }

    WriteLine("-------------------------");
    WriteLine("{0} {1} {2} = {3}", number1, oper, number2, result);
}
catch (Exception exc)
{
    WriteLine("A value or operator you entered is not valid.");
}

WriteLine("================================");

Here is an example of running the application:

Number 1: 837
Operator: +
Number 2: 559
-------------------------
837 + 559 = 1396
================================

Press any key to close this window . . .

Exceptions and Methods

There are various ways you can involve exceptions in a function or method. When it comes to functions and methods, one way you can use the throw option is to get out of the function or method at any time, for any reason. Consider the following code:

class Exercise
{
    public void ShowMessage()
    {
        throw new Exception();

        WriteLine("This is the wonderful world of C# programming.");
    }
}

In this example, the line that holds the message box will never be reached. One way you can proceed in a function or method is to throw an exception if something happens. That is, you can create a conditional statement that would consider, or throw an exception.

Practical LearningPractical Learning: Throwing an Exception from a Method

  1. Change the DistributeParts() method as follows:
    using static System.Console;
    
    (int, int) DistributeParts(int amount, int portion1, int portion2)
    {
        int totalRatios = portion1 + portion2;
    
        if (totalRatios == 0)
            throw new FormatException("The value provided for one or both ratios is not valid.");
    
        int eachPart    = amount / totalRatios;
        int result      = eachPart * portion1;
    
        return (result, amount - result);
    }
    
    int Subtract(int first, int second)
    {
        return first - second;
    }
    
    try
    {
        int amtPledged;
        int ratio1;
        int ratio2;
    
        WriteLine("===================================================");
        WriteLine("Pledge Distribution");
        WriteLine("===================================================");
        WriteLine("Type the values to evaluate a pledge distribution");
        Write("Amount to Allocate:  ");
        amtPledged = int.Parse(ReadLine()!);
        Write("Portion 1:           ");
        ratio1 = int.Parse(ReadLine()!);
        Write("Portion 2:           ");
        ratio2 = int.Parse(ReadLine()!);
    
        (int first, int second) portions = DistributeParts(amtPledged, ratio1, ratio2);
    
        WriteLine("===================================================");
        WriteLine("Pledge Distribution");
        WriteLine("===================================================");
        WriteLine($"Amouont Allocated:   {amtPledged}");
        WriteLine($"Portion 1:           {ratio1}");
        WriteLine($"Portion 2:           {ratio2}");
        WriteLine("---------------------------------------------------");
        WriteLine($"Part 1:              {portions.first}");
        WriteLine($"Part 2:              {portions.second}");
        WriteLine("===================================================");
    }
    catch (FormatException)
    {
        WriteLine("The value you entered for the amount to allocate " +
                  "or pledge is not valid. Only natural numbers " +
                  "(such as 200, 1250, 5000, or 100000) are allowed.");
    }
  2. To execute the project, on the main menu, click Debug -> Start Without Debugging
  3. For the Amount to Allocate, type 8500 and press Enter
  4. For Portion 1, type 0 and press Enter
  5. For Portion 2, type 0 and press Enter
    ===================================================
    Pledge Distribution
    ===================================================
    Type the values to evaluate a pledge distribution
    Amount to Allocate:  8500
    Portion 1:           0
    Portion 2:           0
    The value you entered for the amount to allocate or pledge is not valid. Only natural numbers (such as 200, 1250, 5000, or 100000) are allowed.
    
    Press any key to close this window . . .
  6. To close the window, press X, and return to your programming environment
  7. One of the characteristics of a function is that it returns a value, which is done using the return keyword. Therefore, if a calculation made in a function may produce an error, you can create a conditional statement, such as an if...else or if...else if...else scenario where the last else is a throw condition.
    For an example, change the document as follows:
    using static System.Console;
    
    int DistributeFirstPart(int amount, int portion1, int portion2)
    {
        if (portion1 != 0)
        {
            int totalRatios = portion1 + portion2;
    
            int eachPart = amount / totalRatios;
            int result = eachPart * portion1;
    
            return result;
        }
        else
           throw new FormatException("The value provided for the first ratio portion is not valid.");
    }
    
    int Subtract(int first, int second)
    {
        if (first == 0)
            throw new FormatException("The value provided for the amount to pledge must be a positive number.");
    
        return first - second;
    }
    
    try
    {
        int amtPledged;
        int ratio1;
        int ratio2;
    
        WriteLine("===================================================");
        WriteLine("Pledge Distribution");
        WriteLine("===================================================");
        WriteLine("Type the values to evaluate a pledge distribution");
        Write("Amount to Allocate:  ");
        amtPledged = int.Parse(ReadLine()!);
        Write("Portion 1:           ");
        ratio1 = int.Parse(ReadLine()!);
        Write("Portion 2:           ");
        ratio2 = int.Parse(ReadLine()!);
    
        int part1 = DistributeFirstPart(amtPledged, ratio1, ratio2);
        int part2 = Subtract(amtPledged, part1);
    
        WriteLine("===================================================");
        WriteLine("Pledge Distribution");
        WriteLine("===================================================");
        WriteLine($"Amouont Allocated:   {amtPledged}");
        WriteLine($"Portion 1:           {ratio1}");
        WriteLine($"Portion 2:           {ratio2}");
        WriteLine("---------------------------------------------------");
        WriteLine($"Part 1:              {part1}");
        WriteLine($"Part 2:              {part2}");
        WriteLine("===================================================");
    }
    catch (FormatException)
    {
        WriteLine("The value you entered for the amount to allocate " +
                  "or pledge is not valid. Only natural numbers " +
                  "(such as 200, 1250, 5000, or 100000) are allowed.");
    }
  8. To execute the project, on the main menu, click Debug -> Start Without Debugging
  9. For the Amount to Allocate, type 5000 and press Enter
  10. For Portion 1, type 0 and press Enter
  11. For Portion 2, type 0 and press Enter:
    ===================================================
    Pledge Distribution
    ===================================================
    Type the values to evaluate a pledge distribution
    Amount to Allocate:  5000
    Portion 1:           0
    Portion 2:           0
    The value you entered for the amount to allocate or pledge is not valid. Only natural numbers (such as 200, 1250, 5000, or 100000) are allowed.
    
    Press any key to close this window . . .
  12. To close the window, press X, and return to your programming environment

Exceptions and Properties

If you create a non-automatic property that processes some data and returns a valid result, if there is a possibility that something could go wrong in the calculation, you can make the property throw an exception. You can perform some procesing in the set clause of the property. In that clause, you can throw an exception. Here is an example:

public class WorkDay
{
    private double tw;

    public WorkDay(double time)
    {
        tw = time;
    }

    public double TimeWorked
    {
        get
        {
            return tw;
        }
        set
        {
            if (tw > 24)
                throw new FormatException("Nobody can work more than 24 hours in one day.");

            tw = value;
        }
    }
}

Or you can handle the exception in the get clause.

Nesting an Exception

You can create an exception inside of another. This is referred to as nesting an exception. This is done by applying the same techniques we used to nest conditional statements. This means that you can write an exception that depends on, and is subject to, another exception. To nest an exception, write a try block in the body of the parent exception. The nested try block must be followed by its own catch clause(s). To effectively handle the exception(s), make sure you include an appropriate throw in the try block.

Here is an example:

using static System.Console;

try
{
    double result = 0.00;
    string oper = "";
    double number1 = 0.00, number2 = 0.00;

    Write("Number 1: ");
    number1 = double.Parse(ReadLine()!);
    Write("Operator: ");
    oper = ReadLine()!;
    Write("Number 2: ");
    number2 = double.Parse(ReadLine()!);

    if ((oper != "+") && (oper != "-") && (oper != "*") && (oper != "/"))
    {
        throw new Exception(oper);
    }

    switch (oper)
    {
        case "+":
            result = number1 + number2;
            break;
        case "-":
            result = number1 - number2;
            break;
        case "*":
            result = number1 * number2;
            break;
        case "/":
            try
            {
                if (number2 == 0)
                    throw new DivideByZeroException("Division by zero is not allowed");

                result = number1 / number2;
            }
            catch (DivideByZeroException ex)
            {
                WriteLine(ex.Message);
            }
            break;
        default:
            WriteLine("Bad Operation");
            break;
    }

    WriteLine("-----------------------------------");
    WriteLine("{0} {1} {2} = {3}", number1, oper, number2, result);
}
catch (FormatException)
{
    WriteLine("You type an invalid number. Please correct it");
}
catch (Exception ex)
{
    WriteLine("Operation Error: " + ex.Message + " is not a valid operator");
}

WriteLine("===================================");

Here is an example of running the application:

Number 1: 9284259
Operator: /
Number 2: 731
-----------------------------------
9284259 / 731 = 12700.764705882353
===================================

Press any key to close this window . . .

Practical LearningPractical Learning: Handling Exceptions

  1. Start a new Console App that supports .NET 8.0 (Long-Term Support) and named StellarWaterPoint3
  2. To create a class, in the Solution Explorer, right-click StellarWaterPoint3 -> Add -> Class...
  3. Type WaterBill as the name of the file and class
  4. Click Add
  5. Change the class as follows:
    namespace StellarWaterPoint3
    {
        internal enum Classification { Residential, NonProfit, WaterIntensive, GeneralUse, Other }
    
        internal class WaterBill
        {
            private readonly string typeOfAccount;
    
            public WaterBill(string category) => typeOfAccount = category;
    
            public double CounterReadingStart { get; set; }
            public double CounterReadingEnd   { get; set; }
    
            public (Classification @class, string name) AccountType
            {
                get
                {
                    if (typeOfAccount == "1")
                        return (Classification.Residential, "Residential Household");
                    else if (typeOfAccount == "2")
                        return (Classification.NonProfit, "Social/Non-Profit Organization");
                    else if (typeOfAccount == "3")
                        return (Classification.WaterIntensive, "Water Intensive Business (Laudromat, Hair Salon, etc)");
                    else if (typeOfAccount == "4")
                        return (Classification.GeneralUse, "General Business, Government");
                    else
                        return (Classification.Other, "Other");
                }
            }
    
            public double Gallons => CounterReadingEnd - CounterReadingStart;
    
            public double HCFTotal => Gallons / 748;
    
            public (double First25Therms, double Next15Therms, double Above40Therms) Therms
            {
                get
                {
                    double first, next, above;
    
                    if (HCFTotal is > 40.00)
                    {
                        first = 25.00 * 1.5573;
                        next  = 15.00 * 1.2264;
                        above = (HCFTotal - 40.00) * 0.9624;
                    }
                    else if (HCFTotal > 25.00)
                    {
                        first = 25.00 * 1.5573;
                        next  = (HCFTotal - 25.00) * 1.2264;
                        above = 0.00;
                    }
                    else // if (HCFTotal > 40.00)
                    {
                        first = HCFTotal * 1.5573;
                        next  = 0.00;
                        above = 0.00;
                    }
    
                    return (first, next, above);
                }
            }
    
            public double WaterUsageCharges => Therms.First25Therms + Therms.Next15Therms + Therms.Above40Therms;
    
            public double SewerCharges
            {
                get
                {
                    double result = AccountType.@class switch
                    {
                        Classification.Residential     => WaterUsageCharges * 0.2528,
                        Classification.NonProfit       => WaterUsageCharges * 0.0915,
                        Classification.WaterIntensive  => WaterUsageCharges * 1.5865,
                        Classification.GeneralUse      => WaterUsageCharges * 0.6405,
                        _                              => WaterUsageCharges * 1.2125
                    };
    
                    return result;
                }
            }
    
            public double EnvironmentCharges
            {
                get
                {
                    double result = AccountType.@class switch
                    {
                        Classification.Residential    => WaterUsageCharges * 0.0025,
                        Classification.NonProfit      => WaterUsageCharges * 0.0015,
                        Classification.WaterIntensive => WaterUsageCharges * 0.0105,
                        Classification.GeneralUse     => WaterUsageCharges * 0.0045,
                        _                             => WaterUsageCharges * 0.0125
                    };
    
                    return result;
                }
            }
    
            public double ServiceCharges
            {
                get => AccountType.@class switch
                {
                    Classification.Residential        => WaterUsageCharges * 0.0328,
                    Classification.NonProfit          => WaterUsageCharges * 0.0115,
                    Classification.WaterIntensive     => WaterUsageCharges * 0.1015,
                    Classification.GeneralUse         => WaterUsageCharges * 0.0808,
                    _                                 => WaterUsageCharges * 0.1164
                };
            }
    
            public double TotalCharges => WaterUsageCharges + SewerCharges + EnvironmentCharges + ServiceCharges;
    
            public double LocalTaxes
            {
                get => AccountType.@class switch
                {
                    Classification.Residential        => TotalCharges * 0.115,
                    Classification.NonProfit          => TotalCharges * 0.085,
                    Classification.WaterIntensive     => TotalCharges * 0.825,
                    Classification.GeneralUse         => TotalCharges * 0.315,
                    _                                 => TotalCharges * 0.625
                };
            }
    
            public double StateTaxes => AccountType.@class switch
            {
                Classification.Residential            => TotalCharges * 0.035,
                Classification.NonProfit              => TotalCharges * 0.015,
                Classification.WaterIntensive         => TotalCharges * 0.105,
                Classification.GeneralUse             => TotalCharges * 0.065,
                _                                     => TotalCharges * 0.815
            }; 
    
            public double AmountDue => TotalCharges + LocalTaxes + StateTaxes;
        }
    }
  6. Click the Program tab and change the document as follows:
    using StellarWaterPoint3;
    using static System.Console;
    
    WriteLine("Stellar Water Point");
    WriteLine("===========================================================");
    
    string ? answer = "";
    WaterBill ? bill;
    
    try
    {
        WriteLine("To prepare an invoice, enter the following information");
        WriteLine("Types of Accounts");
        WriteLine("1. Residential Household");
        WriteLine("2. Social/Non-Profit Organization");
        WriteLine("3. Water Intensive Business (Laudromat, etc)");
        WriteLine("4. General Business");
        WriteLine("0. Other");
        Write("Enter Type of Account:         ");
        answer = ReadLine()!;
        WriteLine("-----------------------------------------------------------");
    }
    catch(FormatException fe)
    {
        WriteLine("There was an error from the request for the account type was presented." + Environment.NewLine +
                  "The error produced was: " + fe.Message);
        WriteLine("-----------------------------------------------------------");
    }
    
    bill = new WaterBill(answer);
    
    try
    {
        Write("Counter Reading Start:         ");
        bill.CounterReadingStart = int.Parse(ReadLine()!);
    }
    catch (FormatException fe)
    {
        WriteLine("There was an error from the request for the counter starting number was made." + Environment.NewLine +
                  "The error produced was: " + fe.Message);
        WriteLine("-----------------------------------------------------------");
    }
    
    try
    {
        Write("Counter Reading End:           ");
        bill.CounterReadingEnd = int.Parse(ReadLine()!);
    }
    catch (FormatException fe)
    {
        WriteLine("There was an error from the request for the counter ending number was made." + Environment.NewLine +
                  "The error produced was: " + fe.Message);
        WriteLine("-----------------------------------------------------------");
    }
    
    if( (answer != "") && (bill.CounterReadingStart != 0) && (bill.CounterReadingEnd != 0))
    {
        WriteLine("===========================================================");
        WriteLine("Stellar Water Point - Customer Invoice");
        WriteLine("Account Type:               {0}", bill.AccountType.name);
        WriteLine("===========================================================");
        WriteLine("Meter Reading");
        WriteLine("-----------------------------------------------------------");
        WriteLine("Counter Reading Start:      {0,10}", bill.CounterReadingStart);
        WriteLine("Counter Reading End:        {0,10}", bill.CounterReadingEnd);
        WriteLine("Total Gallons Consumed:     {0,10}", bill.Gallons);
        WriteLine("===========================================================");
        WriteLine("Therms Evaluation");
        WriteLine("-----------------------------------------------------------");
        WriteLine("HCF Total:                  {0,10:n}", bill.HCFTotal);
        WriteLine("First 35 Therms (* 1.5573): {0,10:n}", bill.Therms.First25Therms);
        WriteLine("First 20 Therms (* 1.2264): {0,10:n}", bill.Therms.Next15Therms);
        WriteLine("Above 40 Therms (* 0.9624): {0,10:n}", bill.Therms.Above40Therms);
        WriteLine("-----------------------------------------------------------");
        WriteLine("Water Usage Charges:        {0,10:n}", bill.WaterUsageCharges);
        WriteLine("Sewer Charges:              {0,10:n}", bill.SewerCharges);
        WriteLine("===========================================================");
        WriteLine("Bill Values");
        WriteLine("-----------------------------------------------------------");
        WriteLine("Environment Charges:        {0,10:n}", bill.EnvironmentCharges);
        WriteLine("Service Charges:            {0,10:n}", bill.ServiceCharges);
        WriteLine("Total Charges:              {0,10:n}", bill.TotalCharges);
        WriteLine("Local Taxes:                {0,10:n}", bill.LocalTaxes);
        WriteLine("State Taxes:                {0,10:n}", bill.StateTaxes);
        WriteLine("-----------------------------------------------------------");
        WriteLine("Amount Due:                 {0,10:n}", bill.AmountDue);
    }
    else
    {
        Write("===========================================================");
        WriteLine("The water bill was not prepared appropriately.");
    }
    
    Write("===========================================================");
  7. To execute, on the main menu, click Debug -> Start Without Debugging
  8. When requested, for the type of account, don't type anything and simply press Enter
  9. For the Counter Reading Start, type 26526 and press Enter
  10. For the Counter Reading Start, type 42528 and press Enter
    Stellar Water Point
    ===========================================================
    To prepare an invoice, enter the following information
    Types of Accounts
    1. Residential Household
    2. Social/Non-Profit Organization
    3. Water Intensive Business (Laudromat, etc)
    4. General Business
    0. Other
    Enter Type of Account:
    -----------------------------------------------------------
    Counter Reading Start:         26526
    Counter Reading End:           42528
    ===========================================================
    The water bill was not prepared appropriately.
    ===========================================================
    
    Press any key to close this window . . .
  11. Close the window and return to your programming environment
  12. To execute again, on the main menu, click Debug -> Start Without Debugging
  13. When requested, for the type of account, type 1 and simply press Enter
  14. For the Counter Reading Start, type w and press Enter
  15. For the Counter Reading Start, type 42528 and press Enter
    Stellar Water Point
    ===========================================================
    To prepare an invoice, enter the following information
    Types of Accounts
    1. Residential Household
    2. Social/Non-Profit Organization
    3. Water Intensive Business (Laudromat, etc)
    4. General Business
    0. Other
    Enter Type of Account:         1
    -----------------------------------------------------------
    Counter Reading Start:         w
    There was an error from the request for the counter starting number was made.
    The error produced was: Input string was not in a correct format.
    -----------------------------------------------------------
    Counter Reading End:           42528
    ===========================================================
    The water bill was not prepared appropriately.
    ===========================================================
    Press any key to close this window . . .
  16. Close the window and return to your programming environment
  17. To execute again, on the main menu, click Debug -> Start Without Debugging
  18. When requested, for the type of account, type 1 and simply press Enter
  19. For the Counter Reading Start, type w and press Enter
  20. For the Counter Reading Start, type a and press Enter
    Stellar Water Point
    ===========================================================
    To prepare an invoice, enter the following information
    Types of Accounts
    1. Residential Household
    2. Social/Non-Profit Organization
    3. Water Intensive Business (Laudromat, etc)
    4. General Business
    0. Other
    Enter Type of Account:         1
    -----------------------------------------------------------
    Counter Reading Start:         w
    There was an error from the request for the counter starting number was made.
    The error produced was: Input string was not in a correct format.
    -----------------------------------------------------------
    Counter Reading End:           a
    There was an error from the request for the counter ending number was made.
    The error produced was: Input string was not in a correct format.
    -----------------------------------------------------------
    ===========================================================
    The water bill was not prepared appropriately.
    ===========================================================
    Press any key to close this window . . .
  21. Close the window and return to your programming environment
  22. To execute again, on the main menu, click Debug -> Start Without Debugging
  23. When requested, for the type of account, type 1 and simply press Enter
  24. For the Counter Reading Start, type 41722 and press Enter
  25. For the Counter Reading Start, type 88061 and press Enter
    Stellar Water Point
    ===========================================================
    To prepare an invoice, enter the following information
    Types of Accounts
    1. Residential Household
    2. Social/Non-Profit Organization
    3. Water Intensive Business (Laudromat, etc)
    4. General Business
    0. Other
    Enter Type of Account:         1
    -----------------------------------------------------------
    Counter Reading Start:         41722
    Counter Reading End:           88061
    ===========================================================
    Stellar Water Point - Customer Invoice
    Account Type:               Residential Household
    ===========================================================
    Meter Reading
    -----------------------------------------------------------
    Counter Reading Start:           41722
    Counter Reading End:             88061
    Total Gallons Consumed:          46339
    ===========================================================
    Therms Evaluation
    -----------------------------------------------------------
    HCF Total:                       61.95
    First 35 Therms (* 1.5573):      38.93
    First 20 Therms (* 1.2264):      18.40
    Above 40 Therms (* 0.9624):      21.13
    -----------------------------------------------------------
    Water Usage Charges:             78.45
    Sewer Charges:                   19.83
    ===========================================================
    Bill Values
    -----------------------------------------------------------
    Environment Charges:              0.20
    Service Charges:                  2.57
    Total Charges:                  101.06
    Local Taxes:                     11.62
    State Taxes:                      3.54
    -----------------------------------------------------------
    Amount Due:                     116.21
    ===========================================================
    Press any key to close this window . . .
  26. Close the window and return to your programming environment
  27. Close your programming environment

Previous Copyright © 2001-2024, FunctionX Sunday 05 November 2021 Next