Fundamentals of Debugging

Introduction

So far (in previous lessons), we have learned to debug some values and functions. When it comes to an object, we know that its layout (class, etc) can have fields, properties, and methods. Debuggging can be primarily performed on those members using the same techniques we have already studied.

Practical LearningPractical Learning: Introducing Debugging on an Object

  1. Start Microsoft Visual Studio
  2. Create a new Console App named StellarWaterPoint10
  3. Change the document as follows:
    using static System.Console;
    
    WaterBill bill     = new WaterBill();
    
    bill.InvoiceNumber = 100001;
    
    bill.IdentifyAccount();
    bill.DisplaySummary();
    
    internal enum Classification
    {
        Other,
        Residential,
        GeneralBusiness,
        WaterIntensiveBusiness,
        SocialGovernmentNonProfit,
        UnidentifiedOrUnclassified
    }
    
    internal class WaterBill
    {
        internal int            InvoiceNumber       { get; set; }
        internal double         CounterReadingStart { get; set; }
        internal double         CounterReadingEnd   { get; set; }
        internal Classification Category            { get; set; }
        internal string?        AccountType         { get; set; }
    
        internal string GetAccountType()
        {
            WriteLine("Stellar Water Point");
            WriteLine("==================================================================================");
            WriteLine("To prepare an invoice, enter the following information");
            WriteLine("Types of Accounts");
            WriteLine("1. Residential Household");
            WriteLine("2. General Business");
            WriteLine("3. Social/Government/Non-Profit Organization");
            WriteLine("4. Unidentified or Unclassified Type of Organization");
            WriteLine("5. Water Intensive Business(Laudromat, Hair Salon, Restaurant, etc");
            WriteLine("0. Other");
            Write("Enter Type of Account:       ");
            string acntType = ReadLine()!;
            WriteLine("----------------------------------------------------------------------------------");
    
            return acntType;
        }
    
        internal int ReadCounterStart()
        {
            int value = 0;
            
            try
            {
                Write("Counter Reading Start:       ");
                value = int.Parse(ReadLine()!);
            }
            catch (Exception ex) when (ex is FormatException fex)
            {
                WriteLine("The counter reading start value you entered is not valid.");
                WriteLine("The error produced is: " + fex.Message);
            }
            
            return value;
        }
    
        internal int ReadCounterEnd()
        {
            int value = 0;
            
            try
            {
                Write("Counter Reading End:         ");
                value = int.Parse(ReadLine()!);
            }
            catch (Exception ex) when (ex is FormatException fex)
            {
                WriteLine("The counter reading end value you entered is not valid.");
                WriteLine("The error produced is: " + fex.Message);
            }
            
            return value;
        }
    
        internal void IdentifyAccount()
        {
            string acntType = GetAccountType();
    
            switch (acntType)
            {
                case "1":
                    AccountType = "Residential Household";
                    Category    = Classification.Residential;
                    break;
    
                case "2":
                    AccountType = "General Business";
                    Category    = Classification.GeneralBusiness;
                    break;
    
                case "3":
                    AccountType = "Social/Government/Non-Profit Organization";
                    Category    = Classification.SocialGovernmentNonProfit;
                    break;
    
                case "4":
                    AccountType = "Unidentified or Unclassified Type of Organization";
                    Category    = Classification.UnidentifiedOrUnclassified;
                    break;
    
                case "5":
                    AccountType = "Water Intensive Business(Laudromat, Hair Salon, Restaurant, etc";
                    Category    = Classification.WaterIntensiveBusiness;
                    break;
    
                default:
                    AccountType = "Other";
                    Category    = Classification.Other;
                    break;
            }
    
            CounterReadingStart = ReadCounterStart();
            CounterReadingEnd   = ReadCounterEnd();
        }
    
        internal double Gallons => CounterReadingEnd - CounterReadingStart;
    
        internal double HCFTotal
        {
            // CCF: Centum Cubic Feet
            // HCF: Hundred Cubic Feet
            get
            {
                return Gallons / 748.00;
            }
        }
    
        internal (double First, double Next, double Last) Therms
        {
            get
            {
                double first;
                double next;
                double last;
    
                switch (Category)
                {
                    case Classification.Residential:
                        first = Gallons * 41.50 / 10000.00;
                        next  = Gallons * 32.50 / 10000.00;
                        last  = Gallons * 26.00 / 10000.00;
                        break;
                    case Classification.GeneralBusiness:
                        first = Gallons * 45.00 / 10000.00;
                        next  = Gallons * 30.00 / 10000.00;
                        last  = Gallons * 25.00 / 10000.00;
                        break;
                    case Classification.SocialGovernmentNonProfit:
                        first = Gallons * 46.00 / 10000.00;
                        next  = Gallons * 50.00 / 10000.00;
                        last  = Gallons *  4.00 / 10000.00;
                        break;
                    case Classification.UnidentifiedOrUnclassified:
                        first = Gallons * 25.00 / 10000.00;
                        next  = Gallons * 35.00 / 10000.00;
                        last  = Gallons * 40.00 / 10000.00;
                        break;
                    case Classification.WaterIntensiveBusiness:
                        first = Gallons * 50.00 / 10000.00;
                        next  = Gallons * 40.00 / 10000.00;
                        last  = Gallons * 10.00 / 10000.00;
                        break;
                    default:
                        first = Gallons * (48.00 / 10000.00);
                        next  = Gallons * (32.00 / 10000.00);
                        last  = Gallons * (20.00 / 10000.00);
                        break;
                }
    
                return (first, next, last);
            }
        }
    
        internal double WaterCharges => Therms.First + Therms.Next + Therms.Last;
    
        internal double SewerCharges
        {
            get
            {
                double result = Category switch
                {
                    Classification.Residential                => WaterCharges * 6.826941 / 100,
                    Classification.UnidentifiedOrUnclassified => WaterCharges * 10.6247 / 100,
                    Classification.WaterIntensiveBusiness     => WaterCharges * 12.0535 / 100,
                    Classification.GeneralBusiness            => WaterCharges * 8.3136 / 100,
                    Classification.SocialGovernmentNonProfit  => WaterCharges * 4.162522 / 100,
                    _                                         => WaterCharges * 9.2065 / 100,
                };
    
                return result;
            }
        }
    
        internal double StormCharges
        {
            get
            {
                double result = Category switch
                {
                    Classification.Residential                => WaterCharges * 1.184286 / 100,
                    Classification.UnidentifiedOrUnclassified => WaterCharges * 1.375284 / 100,
                    Classification.WaterIntensiveBusiness     => WaterCharges * 5.606685 / 100,
                    Classification.GeneralBusiness            => WaterCharges * 3.139403 / 100,
                    Classification.SocialGovernmentNonProfit  => WaterCharges * 1.069636 / 100,
                    _                                         => WaterCharges * 0.694748 / 100,
                };
    
                return result;
            }
        }
    
        internal double EnvironmentCharges
        {
            get
            {
                double result = Category switch
                {
                    Classification.Residential                => WaterCharges * .022724,
                    Classification.UnidentifiedOrUnclassified => WaterCharges * .082477,
                    Classification.WaterIntensiveBusiness     => WaterCharges * .413574,
                    Classification.GeneralBusiness            => WaterCharges * .161369,
                    Classification.SocialGovernmentNonProfit  => WaterCharges * 0.118242,
                    _                                         => WaterCharges * 0.221842,
                };
    
                return result;
            }
        }
    
        internal double ServiceCharges
        {
            get => Category switch
            {
                Classification.Residential                => WaterCharges * .145748,
                Classification.UnidentifiedOrUnclassified => WaterCharges * .186692,
                Classification.WaterIntensiveBusiness     => WaterCharges * .412628,
                Classification.GeneralBusiness            => WaterCharges * .242627,
                Classification.SocialGovernmentNonProfit  => WaterCharges * .102246,
                _                                         => WaterCharges * .210248,
            };
        }
    
        internal double TotalCharges
        {
            get
            {
                return WaterCharges + ServiceCharges + StormCharges + EnvironmentCharges + ServiceCharges;
            }
        }
    
        internal double LocalTaxes
        {
            get => Category switch
            {
                Classification.Residential                => TotalCharges * .031574,
                Classification.UnidentifiedOrUnclassified => TotalCharges * .105737,
                Classification.WaterIntensiveBusiness     => TotalCharges * .153248,
                Classification.GeneralBusiness            => TotalCharges * .122517,
                Classification.SocialGovernmentNonProfit  => TotalCharges * .035026,
                _                                         => TotalCharges * .125148,
            };
        }
    
        internal double StateTaxes
        {
            get => Category switch
            {
                Classification.Residential                => TotalCharges * .016724,
                Classification.UnidentifiedOrUnclassified => TotalCharges * .067958,
                Classification.WaterIntensiveBusiness     => TotalCharges * .081622,
                Classification.GeneralBusiness            => TotalCharges * .042448,
                Classification.SocialGovernmentNonProfit  => TotalCharges * .008779,
                _                                         => TotalCharges * .013746,
            };
        }
    
        internal double CalculateAmountDue()
        {
            return TotalCharges + LocalTaxes + StateTaxes;
        }
    
        internal double CalculateLateAmountDue()
        {
            double amt = TotalCharges + LocalTaxes + StateTaxes;
    
            if (Category == Classification.Residential)
            {
                return amt + 8.95;
            }
            else if (Category == Classification.SocialGovernmentNonProfit)
            {
                return amt + (amt / 4.575);
            }
            else if (Category == Classification.GeneralBusiness)
            {
                return amt + (amt / 12.315);
            }
            else if (Category == Classification.UnidentifiedOrUnclassified)
            {
                return amt + (amt / 7.425);
            }
            else if (Category == Classification.WaterIntensiveBusiness)
            {
                return amt + (amt / 15.225);
            }
            else
            {
                return amt + (amt / 6.735);
            }
        }
    
        internal void DisplaySummary()
        {
    
            WriteLine("==================================================================================");
            WriteLine("Stellar Water Point - Customer Invoice");
            WriteLine("----------------------------------------------------------------------------------");
            WriteLine("Water Bill Number:           {0}", InvoiceNumber);
            WriteLine("Account Type:                {0}", AccountType);
            WriteLine("==================================================================================");
            WriteLine("Meter Reading");
            WriteLine("----------------------------------------------------------------------------------");
            WriteLine("Counter Reading Start: {0,10}",   CounterReadingStart);
            WriteLine("Counter Reading End:   {0,10}",   CounterReadingEnd);
            WriteLine("Total Gallons Consumed:{0,10}",   Gallons);
            WriteLine("HCF Total:             {0,10:n}", HCFTotal);
            WriteLine("==================================================================================");
            WriteLine("Therms Evaluation");
            WriteLine("----------------------------------------------------------------------------------");
            WriteLine("First Tier:            {0,10:n}", Therms.First);
            WriteLine("Second Tier:           {0,10:n}", Therms.Next);
            WriteLine("Last Tier:             {0,10:n}", Therms.Last);
            WriteLine("==================================================================================");
            WriteLine("Bill Values");
            WriteLine("----------------------------------------------------------------------------------");
            WriteLine("Water Usage Charges:   {0,10:n}", WaterCharges);
            WriteLine("Sewer Charges:         {0,10:n}", SewerCharges);
            WriteLine("Storm Charges:         {0,10:n}", StormCharges);
            WriteLine("Environment Charges:   {0,10:n}", EnvironmentCharges);
            WriteLine("Service Charges:       {0,10:n}", ServiceCharges);
            WriteLine("Total Charges:         {0,10:n}", TotalCharges);
            WriteLine("----------------------------------------------------------------------------------");
            WriteLine("Local Taxes:           {0,10:n}", LocalTaxes);
            WriteLine("State Taxes:           {0,10:n}", StateTaxes);
            WriteLine("----------------------------------------------------------------------------------");
            WriteLine("Amount Due:            {0,10:n}", CalculateAmountDue());
            WriteLine("Late Amount Due:       {0,10:n}", CalculateLateAmountDue());
            WriteLine("==================================================================================");
        }
    }

Stepping Into an Object

As seen in previous lessons, the primary way to debug an application is to use the Step Into feature. Once you start that, you can use the various tools, namely the windows (Locals, Autos, etc) that assist you while debugging. When it comes to objects, as opposed to variables of primitive types, the windows are equipped to access objects. The members of those objects are also accessed. The values of the properties are displayed. Still, the other techniques we used for variables are available.

Practical LearningPractical Learning: Stepping Into an Object

  1. To start debugging, on the main menu, click Debug -> Step Into.
    Move the DOS window so you can see both your code and that window.
    Notice that the debugging focus is on the class instance (WaterBill bill = new WaterBill();)
  2. To continue debugging, on the main menu, click Debug and click Step Into.
    Click the Autos tab. Notice the node named bill. Click its right-pointing button to expand it.
    Click the Locals tab. Notice the node named bill. Click its right-pointing button to expand it:

    Debugging Objects

Stepping Over a Property

As we know already, the Step Into feature is a good tool to monitor the behavior of variables inside a function. That feature also allows you to know if a function is behaving as expected. In thsame way, you can use the Step Into operation for a property or a property of a class. In some cases, if you have established that a property is behaving as expected, you may want to skip it from debugging. Instead of executing one line at a time, the debugger allows you to execute a whole property at a time or to execute the lines in some properties while skipping the others. To support this, you use a feature named Step Over.

To step over a property, while debugging:

As its name suggests, the Step Over feature allows you to skip a property if you know it doesn't have any problem. When debugging, you choose what properties to step into and which ones to step over.

Practical LearningPractical Learning: Stepping Over

  1. To continue debugging, on the main menu, click Debug and click Step Into.
    A dialog box may display:

    Debugging - Step Into

    If that dialog box comes up, click Yes
  2. To continue debugging, on the main menu, click Debug and click Step Into.
    Notice that the debugging focus moves to the begining of the body of the WaterBill.IdentifyAccount() method
  3. To continue debugging, on the Debug toolbar, click the Step Into button Step Into fifteen times.
    Every time you click, notice the changes in the DOS window
  4. When the type of account is requested in the DOS window, type 1 and press Enter
  5. To continue debugging, on the Debug toolbar, click the Step Into button Step Into five times.
    In the Locals and the Autos window, notice a node named this. Click its right-pointing button to expand it and see the values of the properties
  6. To continue debugging, on the main menu, click Debug -> Step Over
  7. To stop debugging, on the Debug toolbar, click the Step Out button Step Out
  8. When the Counter Reading Start value is requested in the DOS window, type 14968 and press Enter
  9. When the Counter Reading End value is requested in the DOS window, type 18662 and press Enter.
    In the Autos window, expand the bill node and notice the values of the properties:

    Debugging - Step Over

    Click the Locals tab. Expand the bill node to see the values of the properties:

    Debugging - Step Over

  10. To continue debugging, on the Debug toolbar, click the Step Over button Step Into twice:
    Stellar Water Point
    ==================================================================================
    To prepare an invoice, enter the following information
    Types of Accounts
    1. Residential Household
    2. General Business
    3. Social/Government/Non-Profit Organization
    4. Unidentified or Unclassified Type of Organization
    5. Water Intensive Business(Laudromat, Hair Salon, Restaurant, etc
    0. Other
    Enter Type of Account:       1
    ----------------------------------------------------------------------------------
    Counter Reading Start:       14968
    Counter Reading End:         18662
    ==================================================================================
    Stellar Water Point - Customer Invoice
    ----------------------------------------------------------------------------------
    Water Bill Number:           100001
    Account Type:                Residential Household
    ==================================================================================
    Meter Reading
    ----------------------------------------------------------------------------------
    Counter Reading Start:      14968
    Counter Reading End:        18662
    Total Gallons Consumed:      3694
    HCF Total:                   4.94
    ==================================================================================
    Therms Evaluation
    ----------------------------------------------------------------------------------
    First Tier:                 15.33
    Second Tier:                12.01
    Last Tier:                   9.60
    ==================================================================================
    Bill Values
    ----------------------------------------------------------------------------------
    Water Usage Charges:        36.94
    Sewer Charges:               2.52
    Storm Charges:               0.44
    Environment Charges:         0.84
    Service Charges:             5.38
    Total Charges:              48.98
    ----------------------------------------------------------------------------------
    Local Taxes:                 1.55
    State Taxes:                 0.82
    ----------------------------------------------------------------------------------
    Amount Due:                 51.35
    Late Amount Due:            60.30
    ==================================================================================
    
    Press any key to close this window . . .
  11. In the DOS window, press U to close the window and return to your programming environment
  12. Create a new Console App named StellarWaterPoint11
  13. To create a new folder, in the Solution Explorer, right-click StellarWaterPoint11 -> Add -> New Folder
  14. Type Models as the name of the folder
  15. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  16. Type WaterMeter as the name of the file and class
  17. Click Add
  18. Change the class as follows:
    namespace StellarWaterPoint11.Models
    {
        internal record WaterMeter
        {
            internal string MeterNumber { get; set; } = string.Empty;
            internal string Make        { get; set; } = string.Empty;
            internal string Model       { get; set; } = string.Empty;
            internal string MeterSize   { get; set; } = string.Empty;
    
            public override string ToString()
            {
                return "Mtr #: " + MeterNumber + " - " +
                       Make      + " "         + Model +
                       "(Meter Size: " + MeterSize + ")";
            }
        }
    }
  19. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  20. Type Classification as the name of the file and class
  21. Click Add
  22. Change the document as follows:
    namespace StellarWaterPoint11.Models
    {
        internal enum Classification
        {
            Other,
            Residential,
            GeneralBusiness,
            WaterIntensiveBusiness,
            SocialGovernmentNonProfit,
            UnidentifiedOrUnclassified
        }
    }
  23. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  24. Type Customer as the name of the file and class
  25. Click Add
  26. Change the class as follows:
    namespace StellarWaterPoint11.Models
    {
        internal record class Customer
        {
            internal WaterMeter     Meter         { get; set; }
            internal string         AccountNumber { get; set; } = string.Empty;
            internal string         AccountName   { get; set; } = string.Empty;
            internal string         AccountType   { get; set; } = string.Empty;
            internal string         Address       { get; set; } = string.Empty;
            internal string         City          { get; set; } = string.Empty;
            internal string         County        { get; set; } = string.Empty;
            internal string         State         { get; set; } = string.Empty;
            internal string         ZIPCode       { get; set; } = string.Empty;
            internal Classification Category      { get; set; }
    
            internal Customer()
            {
                Meter = new WaterMeter();
            }
    
            public override string ToString()
            {
                return "Account Number:             " + AccountNumber + Environment.NewLine +
                       "Account Name:               " + AccountName + Environment.NewLine +
                       "Account Type:               " + AccountType + Environment.NewLine +
                       "Address:                    " + Address + Environment.NewLine +
                       "City:                       " + City + Environment.NewLine +
                       "County:                     " + County + Environment.NewLine +
                       "State:                      " + State + Environment.NewLine +
                       "ZIP Code:                   " + ZIPCode;
            }
        }
    }
    
  27. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  28. Type WaterBill as the name of the file and class
  29. Click Add
  30. Change the document as follows:
    namespace StellarWaterPoint11.Models
    {
        internal record class WaterBill
        {
            internal int       InvoiceNumber       { get; set; } 
            internal Customer? Client              { get; set; }
            internal double    CounterReadingStart { get; set; }
            internal double    CounterReadingEnd   { get; set; }
    
            internal int ReadCounterStart()
            {
                int value = 0;
    
                try
                {
                    Console.Write("Counter Reading Start:       ");
                    value = int.Parse(Console.ReadLine()!);
                }
                catch (Exception ex) when (ex is FormatException fex)
                {
                    Console.WriteLine("The counter reading start value you entered is not valid.");
                    Console.WriteLine("The error produced is: " + fex.Message);
                }
    
                return value;
            }
    
            internal int ReadCounterEnd()
            {
                int value = 0;
    
                try
                {
                    Console.Write("Counter Reading End:         ");
                    value = int.Parse(Console.ReadLine()!);
                }
                catch (Exception ex) when (ex is FormatException fex)
                {
                    Console.WriteLine("The counter reading end value you entered is not valid.");
                    Console.WriteLine("The error produced is: " + fex.Message);
                }
    
                return value;
            }
    
            internal void IdentifyAccount()
            {
                CounterReadingStart = ReadCounterStart();
                CounterReadingEnd   = ReadCounterEnd();
            }
    
            internal double Gallons => CounterReadingEnd - CounterReadingStart;
    
            internal double HCFTotal
            {
                get
                {
                    return Gallons / 748.05;
                }
            }
    
            internal (double First, double Next, double Last) Therms
            {
                get
                {
                    double first;
                    double next;
                    double last;
    
                    switch (Client!.Category)
                    {
                        case Classification.Residential:
                            first = Gallons * 41.50 / 10000.00;
                            next  = Gallons * 32.50 / 10000.00;
                            last  = Gallons * 26.00 / 10000.00;
                            break;
    
                        case Classification.GeneralBusiness:
                            first = Gallons * 45.00 / 10000.00;
                            next  = Gallons * 30.00 / 10000.00;
                            last  = Gallons * 25.00 / 10000.00;
                            break;
    
                        case Classification.SocialGovernmentNonProfit:
                            first = Gallons * 46.00 / 10000.00;
                            next  = Gallons * 50.00 / 10000.00;
                            last  = Gallons *  4.00 / 10000.00;
                            break;
    
                        case Classification.UnidentifiedOrUnclassified:
                            first = Gallons * 25.00 / 10000.00;
                            next  = Gallons * 35.00 / 10000.00;
                            last  = Gallons * 40.00 / 10000.00;
                            break;
    
                        case Classification.WaterIntensiveBusiness:
                            first = Gallons * 50.00 / 10000.00;
                            next  = Gallons * 40.00 / 10000.00;
                            last  = Gallons * 10.00 / 10000.00;
                            break;
    
                        default:
                            first = Gallons * (48.00 / 10000.00);
                            next  = Gallons * (32.00 / 10000.00);
                            last  = Gallons * (20.00 / 10000.00);
                            break;
                    }
    
                    return (first, next, last);
                }
            }
    
            internal double WaterCharges
            {
                get
                {
                    return Therms.First + Therms.Next + Therms.Last;
                }
            }
    
            internal double SewerCharges
            {
                get
                {
                    double result = Client!.Category switch
                    {
                        Classification.Residential                => WaterCharges *  6.826941 / 100.00,
                        Classification.UnidentifiedOrUnclassified => WaterCharges * 10.624729 / 100.00,
                        Classification.WaterIntensiveBusiness     => WaterCharges * 12.053502 / 100.00,
                        Classification.GeneralBusiness            => WaterCharges *  8.313635 / 100.00,
                        Classification.SocialGovernmentNonProfit  => WaterCharges *  4.162522 / 100.00,
                        _                                         => WaterCharges *  9.206253 / 100.00
                    };
    
                    return result;
                }
            }
    
            internal double StormCharges
            {
                get
                {
                    double result = Client!.Category switch
                    {
                        Classification.Residential                => WaterCharges * 1.184286 / 100.00,
                        Classification.UnidentifiedOrUnclassified => WaterCharges * 1.375284 / 100.00,
                        Classification.WaterIntensiveBusiness     => WaterCharges * 5.606685 / 100.00,
                        Classification.GeneralBusiness            => WaterCharges * 3.139403 / 100.00,
                        Classification.SocialGovernmentNonProfit  => WaterCharges * 1.069636 / 100.00,
                        _                                         => WaterCharges *  .694748 / 100.00,
                    };
    
                    return result;
                }
            }
    
            internal double EnvironmentCharges
            {
                get
                {
                    double result = Client!.Category switch
                    {
                        Classification.Residential                => WaterCharges * .022724,
                        Classification.UnidentifiedOrUnclassified => WaterCharges * .082477,
                        Classification.WaterIntensiveBusiness     => WaterCharges * .413574,
                        Classification.GeneralBusiness            => WaterCharges * .161369,
                        Classification.SocialGovernmentNonProfit  => WaterCharges * .118242,
                        _                                         => WaterCharges * .221842
                    };
    
                    return result;
                }
            }
    
            internal double ServiceCharges
            {
                get => Client!.Category switch
                {
                    Classification.Residential                => WaterCharges * .145748,
                    Classification.UnidentifiedOrUnclassified => WaterCharges * .186692,
                    Classification.WaterIntensiveBusiness     => WaterCharges * .412628,
                    Classification.GeneralBusiness            => WaterCharges * .242627,
                    Classification.SocialGovernmentNonProfit  => WaterCharges * .102246,
                    _                                         => WaterCharges * .210248
                };
            }
    
            internal double TotalCharges
            {
                get
                {
                    return WaterCharges + ServiceCharges + StormCharges + EnvironmentCharges + ServiceCharges;
                }
            }
    
            internal double LocalTaxes
            {
                get => Client!.Category switch
                {
                    Classification.Residential                => TotalCharges * .031574,
                    Classification.UnidentifiedOrUnclassified => TotalCharges * .105737,
                    Classification.WaterIntensiveBusiness     => TotalCharges * .153248,
                    Classification.GeneralBusiness            => TotalCharges * .122517,
                    Classification.SocialGovernmentNonProfit  => TotalCharges * .035026,
                    _                                         => TotalCharges * .125148
                };
            }
    
            internal double StateTaxes
            {
                get => Client!.Category switch
                {
                    Classification.Residential                => TotalCharges * .016724,
                    Classification.UnidentifiedOrUnclassified => TotalCharges * .067958,
                    Classification.WaterIntensiveBusiness     => TotalCharges * .081622,
                    Classification.GeneralBusiness            => TotalCharges * .042448,
                    Classification.SocialGovernmentNonProfit  => TotalCharges * .008779,
                    _                                         => TotalCharges * .013746
                };
            }
    
            internal double CalculateAmountDue()
            {
                return TotalCharges + LocalTaxes + StateTaxes;
            }
    
            internal double CalculateLateAmountDue()
            {
                double amt = TotalCharges + LocalTaxes + StateTaxes;
    
                if (Client!.Category == Classification.Residential)
                {
                    return amt + 8.95;
                }
                else if (Client.Category == Classification.SocialGovernmentNonProfit)
                {
                    return amt + (amt / 4.575);
                }
                else if (Client.Category == Classification.GeneralBusiness)
                {
                    return amt + (amt / 12.315);
                }
                else if (Client.Category == Classification.UnidentifiedOrUnclassified)
                {
                    return amt + (amt / 7.425);
                }
                else if (Client.Category == Classification.WaterIntensiveBusiness)
                {
                    return amt + (amt / 15.225);
                }
                else
                {
                    return amt + (amt / 6.735);
                }
            }
    
            internal void DisplaySummary()
            {
                Console.WriteLine("==================================================================================");
                Console.WriteLine("Stellar Water Point - Water Bill Invoice");
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine("Water Bill Number:          {0}", InvoiceNumber);
                Console.WriteLine("==================================================================================");
                Console.WriteLine("Customer Account Information");
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine(Client);
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine("Meter Information:          {0}", Client!.Meter);
                Console.WriteLine("==================================================================================");
                Console.WriteLine("Water Bill Details");
                Console.WriteLine("==================================================================================");
                Console.WriteLine("Meter Reading");
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine("Counter Reading Start:   {0,10}",   CounterReadingStart);
                Console.WriteLine("Counter Reading End:     {0,10}",   CounterReadingEnd);
                Console.WriteLine("Total Gallons Consumed:  {0,10}",   Gallons);
                Console.WriteLine("HCF Total:               {0,10:n}", HCFTotal);
                Console.WriteLine("==================================================================================");
                Console.WriteLine("Therms Evaluation");
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine("First Tier:              {0,10:n}", Therms.First);
                Console.WriteLine("Second Tier:             {0,10:n}", Therms.Next);
                Console.WriteLine("Last Tier:               {0,10:n}", Therms.Last);
                Console.WriteLine("==================================================================================");
                Console.WriteLine("Bill Values");
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine("Water Usage Charges:     {0,10:n}", WaterCharges);
                Console.WriteLine("Sewer Charges:           {0,10:n}", SewerCharges);
                Console.WriteLine("Storm Charges:           {0,10:n}", StormCharges);
                Console.WriteLine("Environment Charges:     {0,10:n}", EnvironmentCharges);
                Console.WriteLine("Service Charges:         {0,10:n}", ServiceCharges);
                Console.WriteLine("Total Charges:           {0,10:n}", TotalCharges);
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine("Local Taxes:             {0,10:n}", LocalTaxes);
                Console.WriteLine("State Taxes:             {0,10:n}", StateTaxes);
                Console.WriteLine("----------------------------------------------------------------------------------");
                Console.WriteLine("Amount Due:              {0,10:n}", CalculateAmountDue());
                Console.WriteLine("Late Amount Due:         {0,10:n}", CalculateLateAmountDue());
                Console.WriteLine("==================================================================================");
            }
        }
    }
  31. In the Solution Explorer, right-click Program.cs -> Rename
  32. Type BillProcessing (to get BillProcessing.cs) and press Enter
  33. Click the BillProcessing.cs tab and change its document as follows:
    using static System.Console;
    using StellarWaterPoint11.Models;
    
    WriteLine("Stellar Water Point");
    WriteLine("=================================================================================");
    WriteLine("To prepare an invoice, enter the following information");
    
    WaterMeter GetWaterMeter()
    {
        var waterMeter = new WaterMeter();
    
        WriteLine("Enter the information about the water meter");
        Write("Meter Number:               ");
        waterMeter.MeterNumber = ReadLine()!;
        Write("Manufacturer:               ");
        waterMeter.Make        = ReadLine()!;
        Write("Model:                      ");
        waterMeter.Model       = ReadLine()!;
        Write("Meter Size:                 ");
        waterMeter.MeterSize   = ReadLine()!;
    
        return waterMeter;
    }
    
    Customer Identity()
    {
        Customer client = new();
    
        WriteLine("=================================================================================");
        WriteLine("Enter the information about the customer");
        WriteLine("---------------------------------------------------------------------------------");
        Write("Account Number:             ");
        client.AccountNumber = ReadLine()!;
        Write("Account Name:               ");
        client.AccountName   = ReadLine()!;
        WriteLine("---------------------------------------------------------------------------------");
        WriteLine("Types of Accounts");
        WriteLine("1. Residential Household");
        WriteLine("2. General Business");
        WriteLine("3. Social/Government/Non-Profit Organization");
        WriteLine("4. Unidentified or Unclassified Type of Organization");
        WriteLine("5. Water Intensive Business(Laudromat, Hair Salon, Restaurant, etc");
        WriteLine("0. Other");
        Write("Enter Type of Account:      ");
        string acntType = ReadLine()!;
    
        switch (acntType)
        {
            case "1":
                client.AccountType = "Residential Household";
                client.Category    = Classification.Residential;
                break;
    
            case "2":
                client.AccountType = "General Business";
                client.Category    = Classification.GeneralBusiness;
                break;
    
            case "3":
                client.AccountType = "Social/Government/Non-Profit Organization";
                client.Category    = Classification.SocialGovernmentNonProfit;
                break;
    
            case "4":
                client.AccountType = "Unidentified or Unclassified Type of Organization";
                client.Category    = Classification.UnidentifiedOrUnclassified;
                break;
    
            case "5":
                client.AccountType = "Water Intensive Business(Laudromat, Hair Salon, Restaurant, etc";
                client.Category    = Classification.WaterIntensiveBusiness;
                break;
    
            default:
                client.AccountType = "Other";
                client.Category    = Classification.Other;
                break;
        }
    
        WriteLine("---------------------------------------------------------------------------------");
        Write("Address:                    ");
        client.Address = ReadLine()!;
        Write("City:                       ");
        client.City    = ReadLine()!;
        Write("County:                     ");
        client.County  = ReadLine()!;
        Write("State:                      ");
        client.State   = ReadLine()!;
        Write("ZIP Code:                   ");
        client.ZIPCode = ReadLine()!;
    
        return client;
    }
    
    WaterBill waterBill     = new WaterBill();
    
    waterBill.InvoiceNumber = 100002;
    
    WaterMeter waterMeter   = GetWaterMeter();
    Customer   customer     = Identity();
    
    customer.Meter          = waterMeter;
    waterBill.Client        = customer;
    
    WriteLine("=================================================================================");
    WriteLine("Water Bill Preparation");
    WriteLine("Enter the water reading information");
    waterBill.IdentifyAccount();
    Clear();
    waterBill.DisplaySummary();

Debugging Objects

Introduction

Just as we learned to debug one object, you can debug an application that contains many classes that lead to various objects. The techniques we used for debugging one object are the same to be applied to many objects. When it comes to objects, you can create their classes in a single file; but in most cases, the classes are created in many files, sometimes each class in its own file. When you debug such an application, the debugger will know how to access each class and its content (methods and properties).

Practical LearningPractical Learning: Examining Local Variables

  1. To start debugging, in the Solution Explorer, right-click the name of the project -> Debug -> Step Into New Instance.
    Notice that the debugging focus has moved to the first statement.
    Notice the objects in the Locals window:

    The Locals Window - Debugging Objects

  2. To continue debugging, on the Debug toolbar, click the Step Into button Step Into three times.
    Notice that the debugging focus has moved to the first object created (WaterBill waterBill = new WaterBill();)
  3. To continue debugging, on the Debug toolbar, click the Step Into button Step Into.
    In the Locals window, notice the button on the waterBill node:

    Locals Window - Debugging Objects

    Click the right-pointing arrow button to expand the node:

    Locals Window - Debugging Objects

    (Don't pay attention to the red icons; we are not concerned with them)
  4. To continue debugging, on the Debug toolbar, click the Step Over button Step Over
  5. To continue debugging, on the Debug toolbar, click the Step Into button Step Into three times.
    Notice that the debugging focus has moved to the WaterMeter class in the WaterMeter.cs file.
    In the Locals window, notice the this node. Expand it
  6. To continue debugging, on the main menu, click Debug -> Step Into. Do that four times.
    Notice that the debugging focus has returned to the BillProcessing.cs document in the body of the GetWaterMeter() function
  7. To continue debugging, on the main menu, click Debug and click Step Into. Do that three times
  8. To continue debugging, on the main menu, click Debug and click Step Over
  9. When the Meter Number is requested in the DOS window, type 580-742-825 and press Enter
  10. To continue debugging, on the main menu, click Debug and click Step Into
  11. To continue debugging, on the main menu, click Debug and click Step Over
  12. When the Manufacturer value is requested in the DOS window, type Kensa Sons and press Enter
  13. To continue debugging, on the main menu, click Debug and click Step Into
  14. To continue debugging, press F10 (Step Over)
  15. When the value of the Model is requested, type KS2000A and press Enter
  16. To continue debugging, on the main menu, click Debug and click Step Into
  17. To continue debugging, press F10 (Step Over)
  18. When the value of the Meter Size is requested, type 1 3/4 Inches and press Enter
  19. To continue debugging, on the Debug toolbar, click the Step Into button Step Into.
    Notice the tool tip value on the closing curly bracket of the GetWaterMeter() function.
    In the Locals window, if necessary, expand the waterMeter node:

    Locals Window - Debugging Objects

  20. To continue debugging, on the Debug toolbar, click the Step Into button Step Into twenty-seven times.
    Every time you click, notice the contents of the Locals and the Autos window. Whenever the name of an object appears, expand it and observe the values of the properties
  21. To continue debugging, on the main menu, click Debug and click Step Over
  22. In the DOS window, when the Account Number is requested, type 4086-938-4783 and press Enter
  23. To continue debugging, on the main menu, click Debug and click Step Into
  24. To continue debugging, on the main menu, click Debug and click Step Over
  25. In the DOS window, when the Account Name is requested, type Bernotte's Doughnuts and press Enter
  26. To continue debugging, on the Debug toolbar, click the Step Into button Step Into nine times.
    Every time you click, notice the changes in the DOS window, in the Locals and the Autos windows
  27. To continue debugging, on the Debug toolbar, click the Step Over button Step Over
  28. In the DOS window, when the Type of Account is requested, type 4 and press Enter
  29. To continue debugging, on the Debug toolbar, click the Step Into button Step Into
  30. To continue debugging, on the Debug toolbar, click the Step Over button Step Over twice
  31. To continue debugging, on the Debug toolbar, click the Step Into button Step Into four times
  32. To continue debugging, on the main menu, click Debug and click Step Over
  33. In the DOS window, when the Address is requested, type 10103 Hexagon Drive and press Enter
  34. To continue debugging, on the Debug toolbar, click the Step Into button Step Into
  35. To continue debugging, on the Debug toolbar, click the Step Over button Step Over
  36. In the DOS window, when the City is requested, type Winterstown and press Enter
  37. To continue debugging, on the Debug toolbar, click the Step Into button Step Into
  38. To continue debugging, on the Debug toolbar, click the Step Over button Step Over
  39. In the DOS window, when the County is requested, type York and press Enter
  40. To continue debugging, on the Debug toolbar, click the Step Into button Step Into
  41. To continue debugging, on the Debug toolbar, click the Step Over button Step Over
  42. In the DOS window, when the State is requested, type PA and press Enter
  43. To continue debugging, on the Debug toolbar, click the Step Into button Step Into
  44. To continue debugging, on the Debug toolbar, click the Step Over button Step Over
  45. In the DOS window, when the ZIP-Code is requested, type 17402-8818 and press Enter
  46. To continue debugging, on the Debug toolbar, click the Step Into button Step Into.
    Notice the tool tip value on the closing curly bracket of the Identity() function
  47. To continue debugging, on the Debug toolbar, click the Step Into button Step Into
  48. To continue debugging, on the Debug toolbar, click the Step Over button Step Over twice
  49. To continue debugging, on the Debug toolbar, click the Step Into button Step Into ten times
  50. In the DOS window, when the Counter Reading Start value is requested, type 11917 and press Enter
  51. To continue debugging, on the Debug toolbar, click the Step Into button Step Into twice.
    Notice the tool tip value on the closing curly bracket of the ReadCounterStart() function
  52. To continue debugging, on the main menu, click Debug and click Step Into
  53. To continue debugging, on the Debug toolbar, click the Step Over button Step Over twice
  54. In the DOS window, when the Counter Reading End is requested, type 11938 and press Enter.
    Notice the tool tip value on the closing curly bracket of the ReadCounterEnd() function
  55. To continue debugging, on the Debug toolbar, keep clicking the Step Over button Step Over.
    Every time you have clicked, notice the values of the objects and properties in the DOS window, the Locals and the Autos windows.
    Every time you have clicked, position the mouse on various variables in the Code Editor to see their values:
  56. To close the DOS window, press P in it and return to your programming environment

Previous Copyright © 2010-2026, FunctionX Wednesday 15 October 2025, 22:32 Next