A Record With Positional Parameters

Introduction

We have learned that a record can contain one or more constructors. As a simple way to create a record, you can use the formula of a constructor. The formula to follow is:

options record name(parameter-name(s));

As always, start with one or more appropriate options. The most basic option is an access level, such as public. Use the record keyword to indicate that you are creating a record type. Add a name for the record. The name follows the rules of names of classes.

Our formula is as if you are creating a constructor of a record without creating a body delimited by curly brackets. Since a constructor is a method, you must apply parentheses to the name of the record. In the parentheses, create a parameter for each property you would have added if you had created the record in a classic way. End the statement with a semicolon. This technique is referred to as a record with positional parameters. Here is an example:

public record Battery(string PartNumber, string Identification, double Price);

This is the descritîon of a record (as a kind of class or structure) with three properties (two string properties and one double property).

Practical LearningPractical Learning: Introducing Records

  1. Start a new Console App that supports .NET 8.0 (Long-term support) and named StellarWaterPoint9
  2. To create a new folder, in the Solution Explorer, right-click StellarWaterPoint9 -> Add -> New Folder
  3. Type Models as the name of the folder

The Properties of a Record With Positional Parameters

After creating a record as done above, you have a regular record equipped with properties and one constructor that takes a parameter for each property. Every property of such a record is an automatic property with a get and an init clauses. As a normal type, you can use this record to create an object and use it appropriately. For example, you can declare a variable of it and specify a value for each poisitional parameter. Remember that each parameter represents an init property. Therefore, while using a constructor, you must pass an argument for each parameter. Here is an example:

Battery power = new Battery("928359", "ACDelco Gold 94RAGM", 164.95);

Console.Title = "Vehicle Battery";

Console.WriteLine("Vehicle Battery");
Console.WriteLine("=======================================");
Console.WriteLine($"Part #:         {power.PartNumber}");
Console.WriteLine("---------------------------------------");
Console.WriteLine($"Identification: {power.Identification}");
Console.WriteLine($"Price:          {power.Price}");
Console.WriteLine("=======================================");

public record Battery(string PartNumber, string Identification, double Price);

This would produce:

Vehicle Battery
=======================================
Part #:         928359
---------------------------------------
Identification: ACDelco Gold 94RAGM
Price:          164.95
=======================================

Press any key to close this window . . .

Default Positional Parameters

Remember that you must provide a parameter for each property of the record, but each property must be initialized when an object is created. We saw that there are two ways to initialize the object, and one way is with curly brackets. To use this way, you can provide a default value for each parameter. Here is an example:

public record Battery(string PartNumber = "", string Identification = "", double Price = 0.00);

This time, you can use curly brackets to initialize the object. Here is an example:

Battery power = new Battery()
{
    PartNumber = "928359",
    Identification = "ACDelco Gold 94RAGM",
    Price = 164.95
};

Console.Title = "Vehicle Battery";

Console.WriteLine("Vehicle Battery");
Console.WriteLine("=======================================");
Console.WriteLine($"Part #:         {power.PartNumber}");
Console.WriteLine("---------------------------------------");
Console.WriteLine($"Identification: {power.Identification}");
Console.WriteLine($"Price:          {power.Price}");
Console.WriteLine("=======================================");

Console.WriteLine(default(bool));
        
public record Battery(string PartNumber = "", string Identification = "", double Price = 0.00);

With a Record Updated

A record is an immutable type. This means that, once you have created and initialized a record, you cannot change its value(s). What you can do is to make a copy of an existing record. To do this, create another record object and assign an existing record to it. You can then use the new record object like any variable. Here is an example:

Battery power = new Battery()
{
    PartNumber = "928359",
    Identification = "ACDelco Gold 94RAGM",
    Price = 164.95
};

Battery current = power;

Console.Title = "Vehicle Battery";

Console.WriteLine("Vehicle Battery");
Console.WriteLine("=======================================");
Console.WriteLine($"Part #:         {current.PartNumber}");
Console.WriteLine("---------------------------------------");
Console.WriteLine($"Identification: {current.Identification}");
Console.WriteLine($"Price:          {current.Price}");
Console.WriteLine("=======================================");

Console.WriteLine(default(bool));

public record Battery(string PartNumber = "", string Identification = "", double Price = 0.00);

You can then change the value of any property you want and apply that change to the copied record. To do this, you use a keyword named with. The formula to use it is:

record-declaration with { property-1 = value-1, property-2 = value-2, property-x = value-x};

Start with a record object, such as done when declaring a record variable. Add the with keyword followed by curly brackets. End the statement with a semi-colon. Between the curly brackets, type the name of a property of the record or a list of properties. Assign the desired but appropriate value to the property. If you want to specify the values of various properties, separate them with commas. When you perform this exercise, you would get a new record with values changed on the indicated properties. The properties you don't access would keep their values given in the original record object. Here is an example:

using static System.Console;

Battery power = new Battery()
{
    PartNumber = "928359",
    Identification = "ACDelco Gold 94RAGM",
    Price = 164.95
};

Battery current = power with { PartNumber = "479605" };

Title = "Vehicle Battery";

WriteLine("Vehicle Battery");
WriteLine("=======================================");
WriteLine($"Part #:         {current.PartNumber}");
WriteLine("---------------------------------------");
WriteLine($"Identification: {current.Identification}");
WriteLine($"Price:          {current.Price}");
WriteLine("=======================================");

WriteLine(default(bool));

public record Battery(string PartNumber = "", string Identification = "", double Price = 0.00);

This would produce:

Vehicle Battery
=======================================
Part #:         479605
---------------------------------------
Identification: ACDelco Gold 94RAGM
Price:          164.95
=======================================

Press any key to close this window . . .

A Primary Constructor

Introduction

We have just seen that a record allows you to easily create a type with the simplest syntax, all on one line. As convenient as the positional parameter record is, it has its advantages and limitations. One disantage is that apositional parameter record doesn't space to control the values its properties provide.

A Simple Primary Constructor

As seen with classes and structures, a record can use a primary constructor. It is created by adding parentheses to the name of a record, exactly as done when creating a record with positional parameters. A simple primary constructor is created with empty parentheses. Here is an example:

internal record Cube()
{

}

Normally, the reason you create a primary constructor is to make sure the user (the programmer) appropiately initializes an object when creating it and make sure there is no way the object can have a null value. To make this possible, you should provide at least one parameter to the primary constructor. You should create (a) property (properties), usually read-only, that each corresponds to a parameter provided to the primary constructor. In the body of a property, you can simply return the corresponding parameter to the primary constructor. After creating the record, you can create objects from it. You must pass an argument for each parameter of the primary constructor. Here is an example:

>using static System.Console;

void Present(Cube box)
{
    WriteLine("Cube Characteristics");
    WriteLine("-------------------------------------");
    WriteLine("Side:            {0}", box.Side);
    WriteLine("Number of Faces: {0}", box.Faces);
    WriteLine("Number of Edges: {0}", box.Edges);
    WriteLine("Face Area:       {0}", box.FaceArea);
    WriteLine("Total Area:      {0}", box.TotalArea);
}

Cube cb = new Cube(508.6405);

Present(cb);

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

internal record Cube(double side)
{
    internal int Faces
    {
        get
        {
            return 6;
        }
    }

    internal int Edges
    {
        get
        {
            return 12;
        }
    }

    internal double Side
    {
        get
        {
            return side;
        }
    }

    internal double FaceArea
    {
        get
        {
            return side * side;
        }
    }

    internal double TotalArea
    {
        get
        {
            return FaceArea * Faces;
        }
    }
}

This would produce:

Cube Characteristics
-------------------------------------
Side:            508.6405
Number of Faces: 6
Number of Edges: 12
Face Area:       258715.15824024996
Total Area:      1552290.9494414998
=====================================

Press any key to close this window . . .

In the same way, you can create a primary constructor that uses more than one parameter, in which case you would provide a property for each parameter. As we have seen so far, a parameter can be of a primitive type. A parameter can also be a structure, a class, or a record type. In this case, remember to appropriately initialize such a parameter. Here is an example:

using static System.Console;

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

double Create(ref EquilateralTriangle tri)
{
    WriteLine("Enter the values to process the prism");
    Write("Side of Triangular Base:  ");
    tri.Side = double.Parse(ReadLine()!);
    Write("Height of Prism:          ");
    double height = double.Parse(ReadLine()!);

    return height;
}

void Present(TriangularPrism trip)
{
    WriteLine("========================================");
    WriteLine("Geometry - Equilateral Triangle");
    WriteLine("========================================");
    WriteLine("Base");
    WriteLine("----------------------------------------");
    WriteLine("Side:      {0}", trip.Triangle.Side);
    WriteLine("Perimeter: {0}", trip.Triangle.Perimeter);
    WriteLine("Base Area: {0}", trip.Triangle.Area);
    WriteLine("----------------------------------------");
    WriteLine("Standing Effect");
    WriteLine("----------------------------------------");
    WriteLine("Height:    {0}", trip.Height);
    WriteLine("Volume:    {0}", trip.Volume);
    WriteLine("========================================");
}

EquilateralTriangle et = new();

double h = Create(ref et);

TriangularPrism tp = new(et, h);

Present(tp);

internal record TriangularPrism(EquilateralTriangle equiTri, double height)
{
    internal EquilateralTriangle Triangle
    {
        get
        {
            return equiTri;
        }
    }

    internal double Height
    {
        get
        {
            return height;
        }
    }

    internal double Volume
    {
        get
        {
            return equiTri.Area * Height;
        }
    }
}

internal class EquilateralTriangle
{
    private double s;
    const double NumberOfSides = 3;

    public double Side
    {
        get
        {
            return s;
        }

        set
        {
            s = value;
        }
    }

    public double Perimeter
    {
        get
        {
            return s * NumberOfSides;
        }
    }

    public double Area
    {
        get
        {
            return Math.Sqrt(3) * s * s / 4.00;
        }
    }
}

Here is an example of executing the program:

========================================
Enter the values to process the prism
Side of Triangular Base:  248.73
Height of Prism:          157.96
========================================
Geometry - Equilateral Triangle
========================================
Base
----------------------------------------
Side:      248.73
Perimeter: 746.1899999999999
Base Area: 26789.029208749027
----------------------------------------
Standing Effect
----------------------------------------
Height:    157.96
Volume:    4231595.053813997
========================================

Press any key to close this window . . .

Controlling the Properties of Primary Constructor

Consider the following version of the above program:

using static System.Math;
using static System.Console;

void Present(TriangularPrism trip)
{
    WriteLine("========================================");
    WriteLine("Geometry - Equilateral Triangle");
    WriteLine("========================================");
    WriteLine("Base");
    WriteLine("----------------------------------------");
    WriteLine("Side:      {0}", trip.Triangle!.Side);
    WriteLine("Perimeter: {0}", trip.Triangle.Perimeter);
    WriteLine("Base Area: {0}", trip.Triangle.Area);
    WriteLine("----------------------------------------");
    WriteLine("Standing Effect");
    WriteLine("----------------------------------------");
    WriteLine("Height:    {0}", trip.Height);
    WriteLine("Volume:    {0}", trip.Volume);
    WriteLine("========================================");
}

TriangularPrism tp = new();

tp.Height = 157.96;

Present(tp);

internal record TriangularPrism
{
    private EquilateralTriangle? tri;
    private double               height;

    internal EquilateralTriangle Triangle
    {
        set
        {
            tri = value;
        }

        get
        {
            return tri!;
        }
    }

    internal double Height
    {
        get
        {
            return height;
        }

        set
        {
            height = value;
        }
    }

    internal double Volume
    {
        get
        {
            return Triangle!.Area * Height;
        }
    }
}

internal class EquilateralTriangle
{
    private double s;
    const double NumberOfSides = 3;

    public double Side
    {
        get
        {
            return s;
        }

        set
        {
            s = value;
        }
    }

    public double Perimeter
    {
        get
        {
            return s * NumberOfSides;
        }
    }

    public double Area
    {
        get
        {
            return Sqrt(3) * s * s / 4.00;
        }
    }
}

Imagine that, for some reason, an argument is passed with a bad value or null, as is the case for the EquilateralTriangle property (used without being initialized) above. The above program would produce:

========================================
Geometry - Equilateral Triangle
========================================
Base
----------------------------------------
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at Program.. . .\Program.cs:line 11
   at Program.. . .\Program.cs:line 26

Press any key to close this window . . .

While a positional parameter record provides a fast means of creating a record, you cannot let its associated properties validate or reject a value. That control is an advantage of a record created with a body. In the body of a property, you can access fields of a record and initialize them or get their values, you can define a local function and/or call an external function, you can access the methods of the record, you can create switch expressions or conditional statements, etc. This means that you can create a mecanism to validate or reject the value of a property. Here is an example:

using static System.Math;
using static System.Console;

void Present(TriangularPrism trip)
{
    WriteLine("========================================");
    WriteLine("Geometry - Equilateral Triangle");
    WriteLine("========================================");
    WriteLine("Base");
    WriteLine("----------------------------------------");
    WriteLine("Side:      {0}", trip.Triangle!.Side);
    WriteLine("Perimeter: {0}", trip.Triangle.Perimeter);
    WriteLine("Base Area: {0}", trip.Triangle.Area);
    WriteLine("----------------------------------------");
    WriteLine("Standing Effect");
    WriteLine("----------------------------------------");
    WriteLine("Height:    {0}", trip.Height);
    WriteLine("Volume:    {0}", trip.Volume);
    WriteLine("========================================");
}

TriangularPrism tp = new();

tp.Height = 157.96;

Present(tp);

internal record TriangularPrism
{
    private EquilateralTriangle? tri;
    private double               height;

    internal EquilateralTriangle Triangle
    {
        set
        {
            tri = value;
        }

        get
        {
            if (tri is null)
            {
                EquilateralTriangle triangle = new EquilateralTriangle();

                triangle.Side = 0;

                return triangle;
            }

            return tri!;
        }
    }

    internal double Height
    {
        get
        {
            if (tri is null)
            {
                return 0.00;
            }

            return height;
        }

        set
        {
            height = value;
        }
    }

    internal double Volume
    {
        get
        {
            return Triangle!.Area * Height;
        }
    }
}

internal class EquilateralTriangle
{
    private double s;
    const double NumberOfSides = 3;

    public double Side
    {
        get
        {
            return s;
        }

        set
        {
            s = value;
        }
    }

    public double Perimeter
    {
        get
        {
            return s * NumberOfSides;
        }
    }

    public double Area
    {
        get
        {
            return Sqrt(3) * s * s / 4.00;
        }
    }
}

Here is an example of executing the program:

========================================
Geometry - Equilateral Triangle
========================================
Base
----------------------------------------
Side:      0
Perimeter: 0
Base Area: 0
----------------------------------------
Standing Effect
----------------------------------------
Height:    0
Volume:    0
========================================

Press any key to close this window . . .

A Record as a Reference or a Value Type

A Record Object as a Reference

As seen so far, after creating a record, when you create a record object, you can use the new operator to initialize the object. When you do that, the object is a reference type, like that of a class; and the object is stored in the computer's heap. To reinforce this, when creating the record, add the class keyword before the name of the record. Here is an example:

Tractor machine = new();

public record class Tractor
{
    public string? ModelName    { get; set; }
    public float   EnginePower  { get; set; }
    public int     LiftCapacity { get; set; }
    public int     MowerHeight  { get; set; }
    public decimal Price        { get; set; }
}

In the same way, if you are creating a positional parameter record and you want the compiler to treat it as a class, create it as record class. Here is an example:

Battery power = new()
{
    PartNumber = "928359",
    Identification = "ACDelco Gold 94RAGM",
    Price = 164.95
};

public record class Battery(string PartNumber = "", string Identification = "", double Price = 0.00);

A Record Object as a Value Type

We saw that, when you declare a variable of a primitive type (int, float, bool, etc), the value is stored in the stack memory. If you want to use many of such values, you can group them in something called a structure. After creating such a structure, you can declare a variable of it. The values of such a variable are stored in the computer's memory stack. When you create a record object, you may want the values of that object to be stored in the stack instead of the heap. To do this, when creating the record, type the struct keyword before the name of the record. Here is an example:

Canoe fun = new()
{
    Identification = 280_385,
    Make = "Emotion",
    Model = "Wasatch "
};

public record struct Canoe
{
    int id;
    string? _make;
    string? _model;
    int     len;
    double _price;

public Canoe(int number)
{
    Identification = number;
}

    public int Identification
    {
        get
        {
            return id;
        }
        init
        {
            id = value;
        }
    }

    public string? Make
    {
        get { return _make; }
        init { _make = value;  }
    }
    public string? Model
    {
        get { return _model; }
        init { _model = value; }
    }

    public int Length
    {
        get { return len; 
        }
        
        set { len = value;
        }
    }

    public double Price
    {
        get
        {
            return _price;
        }

        set
        {
            _price = value;
        }
    }
}

If you are creating the record with positional parameters and you want its objects to be stored in the stack, create it with record struct.

Practical LearningPractical Learning: Creating and Using Record Variations

  1. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  2. Type WaterMeter as the name of the file and class
  3. Click Add
  4. Change the class as follows:
    namespace StellarWaterPoint8.Models
    {
        internal record struct WaterMeter
        {
            // https://www.flows.com/economy-plastic-water-meter-wm-pc-series/
            internal string? MeterNumber { get; set; }
            internal string? Make        { get; set; }
            internal string? Model       { get; set; }
            internal string? MeterSize   { get; set; }
        }
    }
  5. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  6. Type Customer as the name of the file and class
  7. Click Add
  8. Change the class as follows:
    namespace StellarWaterPoint8.Models
    {
        internal record class Customer
        {
            internal WaterMeter? Meter         { get; set; }
            internal string?     AccountNumber { get; set; }
            internal string?     AccountName   { get; set; }
            internal string?     AccountType   { get; set; }
            internal string?     Address       { get; set; }
            internal string?     City          { get; set; }
            internal string?     County        { get; set; }
            internal string?     State         { get; set; }
            internal string?     ZIPCode       { get; set; }
    
            internal Customer()
            {
                Meter = new WaterMeter();
            }
    
            public string AccountDescription
            {
                get
                {
                    switch (AccountType)
                    {
                        case "1":
                            return "Residential Household";
                        case "2":
                            return "Social/Non-Profit Organization";
                        case "3":
                            return "Water Intensive Business (Laudromat, Hair Salon, etc)";
                        case "4":
                            return "General Business, Government";
                        default:
                            return "Other";
                    }
                }
            }
        }
    }
  9. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  10. Type WaterBill as the name of the file and class
  11. Click Add
  12. Change the class as follows:
    namespace StellarWaterPoint8.Models
    {
    
        internal class WaterBill
        {
            internal Customer? Client { get; set; }
    
            internal WaterBill()
            {
                Client = new Customer();
            }
    
            internal double CounterReadingStart { get; set; }
            internal double CounterReadingEnd   { get; set; }
    
            internal double Gallons  => CounterReadingEnd - CounterReadingStart;
    
            internal double HCFTotal => Gallons / 748;
    
            internal (double FirstTier, double NextTier, double LastTier) 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
                    {
                        first = HCFTotal * 1.5573;
                        next  = 0.00;
                        above = 0.00;
                    }
    
                    return (first, next, above);
                }
            }
    
            internal double WaterUsageCharges => Therms.FirstTier + Therms.NextTier + Therms.LastTier;
    
            internal double SewerCharges
            {
                get
                {
                    double result = Client!.AccountType switch
                    {
                        "1" => WaterUsageCharges * 0.2528,
                        "2" => WaterUsageCharges * 0.0915,
                        "3" => WaterUsageCharges * 1.5865,
                        "4" => WaterUsageCharges * 0.6405,
                        _   => WaterUsageCharges * 1.2125
                    };
    
                    return result;
                }
            }
    
            public double EnvironmentCharges
            {
                get
                {
                    double result = Client!.AccountType switch
                    {
                        "1" => WaterUsageCharges * 0.0025,
                        "2" => WaterUsageCharges * 0.0015,
                        "3" => WaterUsageCharges * 0.0105,
                        "4" => WaterUsageCharges * 0.0045,
                        _   => WaterUsageCharges * 0.0125
                    };
    
                    return result;
                }
            }
    
            public double ServiceCharges
            {
                get => Client!.AccountType switch
                {
                    "1" => WaterUsageCharges * 0.0328,
                    "2" => WaterUsageCharges * 0.0115,
                    "3" => WaterUsageCharges * 0.1015,
                    "4" => WaterUsageCharges * 0.0808,
                    _   => WaterUsageCharges * 0.1164
                };
            }
    
            public double TotalCharges => WaterUsageCharges + SewerCharges + EnvironmentCharges + ServiceCharges;
    
            public double LocalTaxes
            {
                get => Client!.AccountType switch
                {
                    "1" => TotalCharges * 0.115,
                    "2" => TotalCharges * 0.085,
                    "3" => TotalCharges * 0.825,
                    "4" => TotalCharges * 0.315,
                    _   => TotalCharges * 0.625
                };
            }
    
            public double StateTaxes => Client!.AccountType switch
            {
                "1" => TotalCharges * 0.035,
                "2" => TotalCharges * 0.015,
                "3" => TotalCharges * 0.105,
                "4" => TotalCharges * 0.065,
                _   => TotalCharges * 0.815
            };
    
            public double AmountDue => TotalCharges + LocalTaxes + StateTaxes;
        }
    }
  13. In the Solution Explorer, right-click Program.cs and click Rename
  14. Type BillProcessing (to get BillProcessing.cs) and press Enter
  15. Click the BillProcessing.cs tab and change the document as follows:
    using StellarWaterPoint8.Models;
    using static System.Console;
    
    WriteLine("Stellar Water Point");
    WriteLine("=================================================================================");
    
    WaterMeter GetWaterMeter()
    {
        var waterMeter = new WaterMeter();
    
        WriteLine("Enter the following 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 Customer();
        
        WriteLine("=================================================================================");
        WriteLine("Enter the following information about the customer");
        WriteLine("Types of Accounts");
        WriteLine("1. Residential Household");
        WriteLine("2. Laudromat");
        WriteLine("3. Restaurant");
        WriteLine("4. General Business");
        WriteLine("5. Social/Government/Non-Profit Organization");
        WriteLine("0. Unidentified or Unclassified Type of Organization");
        Write("Enter Type of Account:      ");
        client.AccountType = ReadLine()!;
        WriteLine("---------------------------------------------------------------------------------");
        Write("Account Number:             ");
        client.AccountNumber = ReadLine()!;
        Write("Account Name:               ");
        client.AccountName = ReadLine()!;
        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 PrepareInvoice()
    {
        WaterMeter wm = GetWaterMeter();
    
        Customer client = Identity();
        client.Meter = wm;
    
        WaterBill invoice = new();
    
        invoice.Client = client;
    
        WriteLine("=================================================================================");
        WriteLine("Enter the following information about the water bill");
    
        try
        {
            Write("Counter Reading Start:      ");
            invoice.CounterReadingStart = int.Parse(ReadLine()!);
        }
        catch(FormatException)
        {
            WriteLine("The value you entered for the counter reading start was invalid. The application will terminate.");
        }
    
        try
        {
            Write("Counter Reading End:        ");
            invoice.CounterReadingEnd = int.Parse(ReadLine()!);
        }
        catch (FormatException)
        {
            WriteLine("The value you entered for the counter reading end was invalid. The application will terminate.");
        }
    
        invoice.Client = client;
    
        return invoice;
    }
    
    void PresentCustomerInvoice(in WaterBill bill)
    {
        WriteLine("=================================================================================");
        WriteLine("Stellar Water Point - Water Bill Invoice");
        WriteLine("=================================================================================");
        WriteLine("Customer Account Information");
        WriteLine("---------------------------------------------------------------------------------");
        WriteLine("Account Number:             {0}", bill.Client!.AccountNumber);
        WriteLine("Account Name:               {0}", bill.Client.AccountName);
        WriteLine("Account Type:               {0}", bill.Client.AccountDescription);
        WriteLine("Address:                    {0}", bill.Client.Address);
        WriteLine("City:                       {0}", bill.Client.City);
        WriteLine("County:                     {0}", bill.Client.County);
        WriteLine("State:                      {0}", bill.Client.State);
        WriteLine("ZIP Code:                   {0}", bill.Client.ZIPCode);
        WriteLine("---------------------------------------------------------------------------------");
        Write("Meter Information:     Mtr #: ");
        Write(bill.Client.Meter!.Value.MeterNumber);
        Write(" - ");
        Write(bill.Client.Meter.Value.Make);
        Write(" ");
        Write(bill.Client.Meter.Value.Model);
        Write(" (Meter Size: ");
        Write(bill.Client.Meter.Value.MeterSize);
        WriteLine(")");
        WriteLine("=================================================================================");
        WriteLine("Water Bill Details");
        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("HCF Total:             {0,10:n}", bill.HCFTotal);
        WriteLine("=================================================================================");
        WriteLine("Therms Evaluation");
        WriteLine("---------------------------------------------------------------------------------");
        WriteLine("First Therms:          {0,10:n}", bill.Therms.FirstTier);
        WriteLine("Second Therms:         {0,10:n}", bill.Therms.NextTier);
        WriteLine("Last Therms:           {0,10:n}", bill.Therms.LastTier);
        WriteLine("=================================================================================");
        WriteLine("Bill Values");
        WriteLine("---------------------------------------------------------------------------------");
        WriteLine("Water Usage Charges:   {0,10:n}", bill.WaterUsageCharges);
        WriteLine("Sewer Charges:         {0,10:n}", bill.SewerCharges);
        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("---------------------------------------------------------------------------------");
        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);
        WriteLine("=================================================================================");
    }
    
    WaterBill bill = PrepareInvoice();
    
    Clear();
    
    PresentCustomerInvoice(in bill);
  16. To execute, on the main menu, click Debug -> Start Without Debugging
  17. When requested, type the following pieces of information and press Enter after each:
    Meter Number:          297-597
    Manufacturer:          Constance Technologies
    Model:                 TG-4822
    Meter Size:            5/8 inches
    Type of Account:       1
    Account Number:        9279-570-8394
    Account Name:          Thomas Stones
    Address:               13252 Broward Ave #D4
    City:                  Frederick
    County:                Frederick
    State:                 MD
    ZIP Code:              21703-6628
    Counter Reading Start: 137926
    Counter Reading End:   188420

    This would produce:

    =================================================================================
    Stellar Water Point - Water Bill Invoice
    =================================================================================
    Customer Account Information
    ---------------------------------------------------------------------------------
    Account Number:             9279-570-8394
    Account Name:               Thomas Stones
    Account Type:               Residential Household
    Address:                    13252 Broward Ave #D4
    City:                       Frederick
    County:                     Frederick
    State:                      MD
    ZIP Code:                   21703-6628
    ---------------------------------------------------------------------------------
    Meter Information:     Mtr #: 297-597 - Constance Technologies TG-4822 (Meter Size: 5/8 inches)
    =================================================================================
    Water Bill Details
    ---------------------------------------------------------------------------------
    Counter Reading Start:     137926
    Counter Reading End:       188420
    Total Gallons Consumed:     50494
    HCF Total:                  67.51
    =================================================================================
    Therms Evaluation
    ---------------------------------------------------------------------------------
    First Therms:               38.93
    Second Therms:              18.40
    Last Therms:                26.47
    =================================================================================
    Bill Values
    ---------------------------------------------------------------------------------
    Water Usage Charges:        83.80
    Sewer Charges:              21.18
    Environment Charges:         0.21
    Service Charges:             2.75
    Total Charges:             107.94
    ---------------------------------------------------------------------------------
    Local Taxes:                12.41
    State Taxes:                 3.78
    ---------------------------------------------------------------------------------
    Amount Due:                124.13
    =================================================================================
    
    Press any key to close this window . . .
  18. Close the window and return to your programming environment
  19. Close your programming environment

Previous Copyright © 2001-2024, FunctionX Saturday 31 December 2023 Next