Fundamentals of Tuples and Classes

Introduction

We now know how to declare tuple variables. In the same way, you can declare a tuple variable in the body of a class, in which case the variable is treated as a field. You can declare such a variable in a regular class created in a normal code file. Here is an example:

namespace PracticalDistribution.Pages
{
    public class Processor
    {
        /* We are combining these pieces of information of the 
         * processor because processors specifications are 
         * related by generation and tied to a manufacturer. */
        (string make, string model) identification;
    }
}

You can declare the variable in a class created in a razor page. Here is an example:

@functions
{
    public class Country
    {
        (string iso, string name, int area) identification;
    }
}

You can also declare the variable in the class of a razor page. Here is an example:

using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Exercises.Pages
{
    public class PoliticalPartyModel : PageModel
    {
        (string fname, string lname) leader;

        public void OnGet()
        {
        }
    }
}

Practical LearningPractical Learning: Introducing Conditions

  1. Start Microsoft Visual Studio and create a new ASP.NET Core Web App project that supports .NET 6.0 (Long-Term Support) with the project named StellarWaterPoint04. Uncheck Configure For TTPS
  2. In the Solution Explorer, right-click StellarWaterPoint5 -> Add -> New Folder
  3. Type Models
  4. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  5. In the middle list of the Add New Item dialog box, make sure Class is selected.
    Change the file Name to WaterBill
  6. Press Enter

Initializing a Tuple

As you may know already, a constructor is a special method that is used to initialize a variable or a field. Therefore, if you create a regular tuple field, you can use a constructor to initialize it. Here is an example:

@functions
{
    public class Processor
    {
        /* We are combining these pieces of information of the 
         * processor because processors specifications are 
         * related by generation and tied to a manufacturer. */
        (string make, string model) identification;
    
        public Processor()
        {
    	   identification = ("HP", "XL2X0 GEN9 E5-2620V3");
        }
    }
}

A Read-Only Tuple

You can create a constant tuple but whose value depends on specific objects. This is the case for a read-only tuple. You can create it in the body of a class and initialize it in a constructor. Here is an example:

@functions
{
    public class Memory
    {
        public readonly (int numberOfSockets, int memoryInGB) partition;

        public Memory()
        {
            partition = (2, 8);
        }

        public Memory(int capacity)
        {
            partition = (2, 16);
        }
    }
}

Initializing the Properties of a Class

Consider a class as follows:

@functions
{
    public class Trapezoid
    {
        public double Bottom { get; set; }
        public double Top    { get; set; }
        public double Height { get; set; }

        public Trapezoid(double bottom, double height)
        {
            Bottom = bottom;
            Height = height;
        }

        public Trapezoid(double bottom, double top, double height)
        {
            Bottom = bottom;
            Top    = top;
            Height = height;
        }
    }
}

Notice that the class has an overloaded constructor with one constructor that takes two arguments and the constructor that takes three arguments. Also notice that, to initialize the properties, each constructor assigns the desired value or a parameterized value to a property. Using the characteristics of a tuple, you can initialize the properties by putting them in parentheses and assigning another set of parentheses to it. In the second parentheses, put a list of the values for the properties (the appropriate value for each parameter that represents a property). The values must be in the order of the properties in the first set of parentheses. These can be done as follows:

@functions
{
    public class Trapezoid
    {
        private double _top_;

        public double Bottom { get; set; }
        public double Height { get; set; }

        public Trapezoid(double bottom, double height)
        {
            (Bottom, Height) = (bottom, height);
        }

        public Trapezoid(double bottom, double top, double height)
        {
            (Bottom, Top, Height) = (bottom, top, height);
        }

        public double Top
        {
            get
            {
                if (_top_ == 0.00)
                    return Bottom * .75;

                return _top_;
            }
            set
            {
                _top_ = value;
            }
        }

        public double Area
        {
            get
            {

                return ((Bottom + Top) / 2.00) * Height;
            }
        }
    }
}

When creating an object, you can use the constructor you want and specify the appropriate values for the parameters. Here are two examples that call each constructor:

@page
@model PracticalDistribution.Pages.ExerciseModel
@{
    Trapezoid trap = new Trapezoid(708.83, 140.07);
}

<pre>Trapezoid
---------------------------
Top:    @trap.Top
Bottom: @trap.Bottom
Height: @trap.Height
---------------------------
Area:   @trap.Area
===========================</pre>

@{
    trap = new Trapezoid(708.83, 486.39, 140.07);
}

<pre>Trapezoid"
---------------------------
Top:    @trap.Top
Bottom: @trap.Bottom
Height: @trap.Height
---------------------------
Area:   @trap.Area
===========================</pre>

This would produce:

Trapezoid
---------------------------
Top:    531.6225000000001
Bottom: 708.83
Height: 140.07
---------------------------
Area:   86875.0908375);
===========================

Trapezoid"
---------------------------
Top:    486.39
Bottom: 708.83
Height: 140.07
---------------------------
Area:   83707.2327

Method Overloading and Tuples

In our introduction to method overloading, we saw that a method can get overloaded if you create more than one version in the same class. We also saw that the versions of the method must differ by their syntax or signature. Remember that the signature of a method doesn't include its return type. Based on this, you cannot overload a method based on the fact that one version returns a tuple and another does not.

To overload a method that involves tuples, you will rely on the parameters. You can create a method that has different versions. Two or more versions of a method can take one tuple parameter each; each tuple-parameter can have the same number of elements but the elements can be different. Consider the following example:

@functions
{
    public class Algebra
    {
        /* The following two versions of an overloaded method take one tuple 
         * as parameter, but each tuple-parameter has different types of parameters. */
        void Calculate((string, string) a)
        {
        }

        void Calculate((string, double) a)
        {
        }

        /* The following additional three versions of an overloaded method take 
         * one tuple as parameter, but each tuple-parameter has different number of parameters. */
        void Calculate((int, bool) a)
        {
        }

        void Calculate((string, int, double) a)
        {
        }

        void Calculate((string, int, bool, double) a)
        {
        }
    }
}

On the other hand, you can overload a method by passing a mixture of primitive types and tuples.

Tuples and Properties

Introduction

You can create a property whose type is a tuple. To start, in the body of the class, you can declare a field variable that is a tuple type. Here is an example:

@functions
{
    public class Member
    {
        public (int, string, double) id;
    }
}

To get the property, create a property that has a return type as a tuple. Here is an example:

public class Member
{
    private (int, string, double) id;

    public (int, string, double) Identification
    {
    }
}

A Read-Only Tuple Property

A read-only property is a property with only a get clause. For a tuple property, make the get clause return a private field that has the same return type as the property. Here is an example:

public class Member
{
    private (int, string, double) id;

    public (int, string, double) Identification
    {
        get
        {
            return id;
        }
    }
}

If you want to use the property outside its class, it must have a value, which it cannot get on its own because it is a read-only property. The most common way you can initialize this property is by using a constructor that uses a parameter of the same tuple type as the property. After initializing the property, you can get its value and use it. Here is an example of a class:

namespace PracticalDistribution.Pages
{
    public class Member
    {
        private (int, string, double) id;

        public Member((int, string, double) identifier, int amt)
        {
            id = identifier;
        }

        public (int number, string name, double fee) Identification
        {
            get
            {
                return id;
            }
        }
    }
}

An object can then be created as follows:

@page
@model PracticalDistribution.Pages.ExerciseModel
@{
    (int nbr, string name, double fee) account = (295_380, "Elisabeth Merlins", 98.50);
    Member mbr = new Member(account, 0);
}

<pre>Membership Details
-------------------------------
Member #:    @mbr.Identification.number
Full Name:   @mbr.Identification.name
Account Fee: @mbr.Identification.fee
==============================="</pre>

This would produce:

Membership Details
-------------------------------
Member #:    295380
Full Name:   Elisabeth Merlins
Account Fee: 98.5
===============================

Practical LearningPractical Learning: Creating a Read-Only Property

  1. Change the class as follows:
    namespace StellarWaterPoint04.Models
    {
        public class WaterBill
        {
            private double nd;
            private double crs;
            private string typeOfAccount;
    
            public string AccountType
            {
                get
                {
                    return typeOfAccount;
                }
    
                set
                {
                    typeOfAccount = value;
                }
            }
    
            public double CounterReadingStart
            {
                get
                {
                    return crs;
                }
    
                set
                {
                    crs = value;
                }
            }
    
            public double CounterReadingEnd
            {
                set
                {
                    nd = value;
                }
                
                get
                {
                    return nd;
                }
            }
    
            public double Gallons
            {
                get
                {
                    return CounterReadingEnd - CounterReadingStart;
                }
            }
    
            public double HCFTotal
            {
                get
                {
                    return Gallons / 748;
                }
            }
    
            public (double First25Therms, double Next15Therms, double Above40Therms) Therms
            {
                get
                {
                    double first, next, above;
    
                    if (HCFTotal > 40.00)
                    {
                        first = 25.00 * 0.53713;
                        next = 15.00 * 0.36149;
                        above = (HCFTotal - 40.00) * 0.1624;
                    }
                    else if (HCFTotal > 25.00)
                    {
                        first = 25.00 * 0.53713;
                        next = (HCFTotal - 25.00) * 0.36149;
                        above = 0.00;
                    }
                    else // if (HCFTotal > 40.00)
                    {
                        first = HCFTotal * 0.53713;
                        next = 0.00;
                        above = 0.00;
                    }
    
                    return (first, next, above);
                }
            }
    
            public double WaterUsageCharges
            {
                get
                {
                    return Therms.First25Therms + Therms.Next15Therms + Therms.Above40Therms;
                }
            }
    
            public double SewerCharges
            {
                get
                {
                    double charges;
    
                    switch (AccountType)
                    {
                        case "residential":
                            charges = WaterUsageCharges * 0.2528 / 2.00;
                            break;
                        case "npo":
                            charges = WaterUsageCharges * 0.0915 / 2.00;
                            break;
                        case "intensive":
                            charges = WaterUsageCharges * 1.5865 / 2.00;
                            break;
                        case "general":
                            charges = WaterUsageCharges * 0.6405 / 2.00;
                            break;
                        default:
                            charges = WaterUsageCharges * 1.2125 / 2.00;
                            break;
                    }
    
                    return charges;
                }
            }
    
            public double EnvironmentCharges
            {
                get
                {
                    double charges;
    
                    switch (AccountType)
                    {
                        case "residential":
                            charges = WaterUsageCharges * 0.0000582;
                            break;
                        case "npo":
                            charges = WaterUsageCharges * 0.0010533;
                            break;
                        case "intensive":
                            charges = WaterUsageCharges * 0.0284257;
                            break;
                        case "general":
                            charges = WaterUsageCharges * 0.0018415;
                            break;
                        default:
                            charges = WaterUsageCharges * 0.0033586;
                            break;
                    }
    
                    return charges;
                }
            }
    
            public double ServiceCharges
            {
                get
                {
                    double charges;
    
                    switch (AccountType)
                    {
                        case "residential":
                            charges = WaterUsageCharges * 0.01368;
                            break;
                        case "npo":
                            charges = WaterUsageCharges * 0.008135;
                            break;
                        case "intensive":
                            charges = WaterUsageCharges * 0.160185;
                            break;
                        case "general":
                            charges = WaterUsageCharges * 0.052208;
                            break;
                        default:
                            charges = WaterUsageCharges * 0.137164;
                            break;
                    }
    
                    return charges;
                }
            }
    
            public double TotalCharges
            {
                get
                {
                    return WaterUsageCharges + SewerCharges + EnvironmentCharges + ServiceCharges;
                }
            }
    
            public double LocalTaxes
            {
                get
                {
                    double charges;
    
                    switch (AccountType)
                    {
                        case "residential":
                            charges = TotalCharges * 0.000316;
                            break;
                        case "npo":
                            charges = TotalCharges * 0.001852;
                            break;
                        case "intensive":
                            charges = TotalCharges * 0.013057;
                            break;
                        case "general":
                            charges = TotalCharges * 0.008253;
                            break;
                        default:
                            charges = TotalCharges * 0.048615;
                            break;
                    }
    
                    return charges;
                }
            }
    
            public double StateTaxes
            {
                get
                {
                    double charges;
    
                    switch (AccountType)
                    {
                        case "residential":
                            charges = TotalCharges * 0.00217;
                            break;
                        case "npo":
                            charges = TotalCharges * 0.00183;
                            break;
                        case "intensive":
                            charges = TotalCharges * 0.08258;
                            break;
                        case "general":
                            charges = TotalCharges * 0.00615;
                            break;
                        default:
                            charges = TotalCharges * 0.008624;
                            break;
                    }
    
                    return charges;
                }
            }
    
            public double AmountDue
            {
                get
                {
                    return TotalCharges + LocalTaxes + StateTaxes;
                }
            }
        }
    }
  2. In the Solution Explorer, right-click Pages -> Add -> Razor Page...
  3. In the Add New Scaffolded Item dialog box, make sure Razor Page - Empty is selected. Click Add
  4. Change the file Name to CustomerInvoice
  5. Press Enter
  6. Change the document as follows:
    @page
    @model StellarWaterPoint04.Pages.CustomerInvoiceModel
    @using StellarWaterPoint04.Models
    @{
        string accountType           =  "0. Other";
        WaterBill bill               = new WaterBill();
        
        string strHCFTotal           = "0.00";
        string strWaterUsageCharges  = "0.00", strSewerCharges = "0.00";
        string strServiceCharges     = "0.00", strEnvironmentCharges = "0.00";
        string strTotalCharges       = "0.00", strAmountDue = "0.00";
        string strStateTaxes         = "0.00", strLocalTaxes = "0.00";
        string strFirst25Therms      = "0.00", strNext15Therms = "0.00", strAbove40Therms = "0.00";
    
        if (Request.HasFormContentType)
        {
            bill.AccountType         = Request.Form["selAccountsTypes"];
            bill.CounterReadingStart = double.Parse(Request.Form["txtCounterReadingStart"]);
            bill.CounterReadingEnd   = double.Parse(Request.Form["txtCounterReadingEnd"]);
    
            strFirst25Therms         = $"{bill.Therms.First25Therms:F}";
            strNext15Therms          = $"{bill.Therms.Next15Therms:F}";
            strAbove40Therms         = $"{bill.Therms.Above40Therms:F}";
            strHCFTotal              = $"{bill.HCFTotal:F}";
            strWaterUsageCharges     = $"{bill.WaterUsageCharges:F}";
            strSewerCharges          = $"{bill.SewerCharges:F}";
            strServiceCharges        = $"{bill.ServiceCharges:F}";
            strEnvironmentCharges    = $"{bill.EnvironmentCharges:F}";
            strTotalCharges          = $"{bill.TotalCharges:F}";
            strStateTaxes            = $"{bill.StateTaxes:F}";
            strLocalTaxes            = $"{bill.LocalTaxes:F}";
            strAmountDue             = $"{bill.AmountDue:F}";
        }
    }
    
    <h2 style="text-align: center">Stellar Water Point - Customer Invoice</h2>
    
    <form method="post" name="frmCustomerInvoice">
        <table style="width: 700px" align="center">
            <tr>
                <td style="width: 200px">Select Type of Account:</td>
                <td>
                    <select id="selAccountsTypes" name="selAccountsTypes">
                        <option value="other">0. Other</option>
                        <option value="residential">1. Residential Household</option>
                        <option value="npo">2. Social/Non-Profit Organization</option>
                        <option value="intensive">3. Water Intensive Business (Laundromat, etc)</option>
                        <option value="general">4. Government/General Business</option>
                    </select>
                </td>
            </tr>
        </table>
        
        <hr />
        
        <h3>Meter Reading</h3>
        
        <hr />
        
        <table style="width: 700px" align="center">
            <tr>
                <td style="width: 200px">Counter Reading Start:</td>
                <td><input type="text" style="width: 100px" id="txtCounterReadingStart"
                           name="txtCounterReadingStart" value="@bill.CounterReadingStart" /></td>
                <td style="width: 200px">Counter Reading End:</td>
                <td><input type="text" style="width: 100px" id="txtCounterReadingEnd"
                           name="txtCounterReadingEnd" value="@bill.CounterReadingEnd" /></td>
            </tr>
        </table>
        
        <hr />
        
        <table style="width: 300px" align="center">
            <tr>
                <td style="width: 50px">&nbsp;</td>
                <td><input type="submit" value="Prepare Water Bill" name="btnPrepare" style="width: 150px" /></td>
            </tr>
        </table>
    </form>
        
    <hr />
    
    <table style="width: 700px" align="center">
        <tr>
            <td style="width: 200px">Total Gallons Consumed:</td>
            <td style="width: 105px">@bill.Gallons</td>
            <td>HCF Total:</td>
            <td>@strHCFTotal</td>
        </tr>
    </table>
        
    <hr />
    
    <h3>Therms Evaluation</h3>
        
    <hr />
    
    <table style="width: 700px" align="center">
        <tr>
            <td>First 25 Therms: @strFirst25Therms</td>
            <td style="width: 25px">&nbsp;</td>
            <td>Next 15 Therms: @strNext15Therms</td>
            <td>&nbsp;</td>
            <td>Above 40 Therms: @strAbove40Therms</td>
        </tr>
    </table>
        
    <hr />
    
    <h3>Bill Values</h3>
        
    <hr />
    
    <table style="width: 700px" align="center">
        <tr>
            <td style="width: 250px">Water Usage Charges:</td>
            <td style="width: 150px">@strWaterUsageCharges</td>
            <td>Sewer Charges:</td>
            <td>@strSewerCharges</td>
        </tr>
        <tr>
            <td>Environment Charges:</td>
            <td>@strEnvironmentCharges</td>
            <td>Service Charges:</td>
            <td>@strServiceCharges</td>
        </tr>
    </table>
    
    <hr />
    
    <table style="width: 700px" align="center">
        <tr>
            <td style="width: 250px"></td>
            <td style="width: 150px"></td>
            <td>Total Charges:</td>
            <td>@strTotalCharges</td>
        </tr>
    </table>
    
    <hr />
    
    <table style="width: 700px" align="center">
        <tr>
            <td style="width: 250px"></td>
            <td>Local Taxes:</td>
            <td>@strLocalTaxes</td>
            <td></td>
            <td>State Taxes:</td>
            <td>@strStateTaxes</td>
        </tr>
    </table>
        
    <hr />
    
    <table style="width: 700px" align="center">
        <tr>
            <td style="width: 250px"></td>
            <td style="width: 150px"></td>
            <td>Amount Due:</td>
            <td>@strAmountDue</td>
        </tr>
    </table>
  7. To execute, on the main menu, click Debug -> Start Without Debugging:

    Tuples and Classes

  8. In the address bar of the browser, click the right side of the address, type /CustomerInvoice and press Enter:

    Tuples and Classes

  9. In the Select Type of Account combo box, select 1. Residential Household
  10. In the Counter Reading Start text box, type 92863
  11. For the Counter Reading Start, type 224926

    Tuples and Classes

  12. Click the Prepare Water Bill button:

    Tuples and Classes

  13. Press Enter to close the window and return to your programming environment
  14. Click the WaterBill tab
  15. To reduce the number of lines of code, change the class as follows:
    namespace StellarWaterPoint04.Models
    {
        public class WaterBill
        {
            private double nd;
            private double crs;
            private string typeOfAccount;
    
            public string AccountType
            {
                get { return typeOfAccount;  }
                set { typeOfAccount = value; }
            }
    
            public double CounterReadingStart
            {
                get { return crs;  }
                set { crs = value; }
            }
    
            public double CounterReadingEnd
            {
                set { nd = value; }
                get { return nd;  }
            }
    
            public double Gallons
            {
                get => CounterReadingEnd - CounterReadingStart;
            }
    
            public double HCFTotal
            {
                get => Gallons / 748;
            }
    
            public (double First25Therms, double Next15Therms, double Above40Therms) Therms
            {
                . . .
            }
    
            public double WaterUsageCharges
            {
                get => Therms.First25Therms + Therms.Next15Therms + Therms.Above40Therms;
            }
    
            public double SewerCharges
            {
                . . .
            }
    
            public double EnvironmentCharges
            {
                . . .
            }
    
            public double ServiceCharges
            {
                . . .
            }
    
            public double TotalCharges
            {
                get => WaterUsageCharges + SewerCharges + EnvironmentCharges + ServiceCharges;
            }
    
            public double LocalTaxes
            {
                . . .
            }
    
            public double StateTaxes
            {
                . . .
            }
    
            public double AmountDue
            {
                get => TotalCharges + LocalTaxes + StateTaxes;
            }
        }
    }
  16. To execute, on the main menu, click Debug -> Start Without Debugging. If the browser presents a Resend button, click it
  17. In the Select Account Type combo box, select 2. Social/Non-Profit Organization
  18. Click the Prepare Water Bill button

    Tuples and Classes

  19. Return to your programming environment
  20. To improve the switch statements, change the WaterBill class as follows:
    namespace StellarWaterPoint04.Pages
    {
        public class WaterBill
        {
            private double nd;
            private double crs;
            private string typeOfAccount;
    
            public string AccountType
            {
                get { return typeOfAccount;  }
                set { typeOfAccount = value; }
            }
    
            public double CounterReadingStart
            {
                get { return crs;  }
                set { crs = value; }
            }
    
            public double CounterReadingEnd
            {
                set { nd = value; }
                get { return nd;  }
            }
    
            public double Gallons
            {
                get => CounterReadingEnd - CounterReadingStart;
            }
    
            public double HCFTotal
            {
                get => Gallons / 748;
            }
    
            public (double First25Therms, double Next15Therms, double Above40Therms) Therms
            {
                get
                {
                    double first, next, above;
    
                    if (HCFTotal > 40.00)
                    {
                        first = 25.00 * 0.53713;
                        next = 15.00 * 0.36149;
                        above = (HCFTotal - 40.00) * 0.1624;
                    }
                    else if (HCFTotal > 25.00)
                    {
                        first = 25.00 * 0.53713;
                        next = (HCFTotal - 25.00) * 0.36149;
                        above = 0.00;
                    }
                    else // if (HCFTotal > 40.00)
                    {
                        first = HCFTotal * 0.53713;
                        next = 0.00;
                        above = 0.00;
                    }
    
                    return (first, next, above);
                }
            }
    
            public double WaterUsageCharges
            {
                get => Therms.First25Therms + Therms.Next15Therms + Therms.Above40Therms;
            }
    
            public double SewerCharges
            {
                get
                {
                    switch (AccountType)
                    {
                        case "residential":
                            return WaterUsageCharges * 0.2528 / 2.00;
                        case "npo":
                            return WaterUsageCharges * 0.0915 / 2.00;
                        case "intensive":
                            return WaterUsageCharges * 1.5865 / 2.00;
                        case "general":
                            return WaterUsageCharges * 0.6405 / 2.00;
                        default:
                            return WaterUsageCharges * 1.2125 / 2.00;
                    }
                }
            }
    
            public double EnvironmentCharges
            {
                get
                {
                    switch (AccountType)
                    {
                        case "residential":
                            return WaterUsageCharges * 0.0000582;
                        case "npo":
                            return WaterUsageCharges * 0.0010533;
                        case "intensive":
                            return WaterUsageCharges * 0.0284257;
                        case "general":
                            return WaterUsageCharges * 0.0018415;
                        default:
                            return WaterUsageCharges * 0.0033586;
                    }
                }
            }
    
            public double ServiceCharges
            {
                get
                {
                    switch (AccountType)
                    {
                        case "residential":
                            return WaterUsageCharges * 0.013683;
                        case "npo":
                            return WaterUsageCharges * 0.008135;
                        case "intensive":
                            return WaterUsageCharges * 0.160185;
                        case "general":
                            return WaterUsageCharges * 0.052208;
                        default:
                            return WaterUsageCharges * 0.137164;
                    }
                }
            }
    
            public double TotalCharges
            {
                get => WaterUsageCharges + SewerCharges + EnvironmentCharges + ServiceCharges;
            }
    
            public double LocalTaxes
            {
                get
                {
                    switch (AccountType)
                    {
                        case "residential":
                            return TotalCharges * 0.000316;
                        case "npo":
                            return TotalCharges * 0.001852;
                        case "intensive":
                            return TotalCharges * 0.013057;
                        case "general":
                            return TotalCharges * 0.008253;
                        default:
                            return TotalCharges * 0.048615;
                    }
                }
            }
    
            public double StateTaxes
            {
                get
                {
                    switch (AccountType)
                    {
                        case "residential":
                            return TotalCharges * 0.00217;
                        case "npo":
                            return TotalCharges * 0.00183;
                        case "intensive":
                            return TotalCharges * 0.08258;
                        case "general":
                            return TotalCharges * 0.00615;
                        default:
                            return TotalCharges * 0.008624;
                    }
                }
            }
    
            public double AmountDue
            {
                get => TotalCharges + LocalTaxes + StateTaxes;
            }
        }
    }
  21. To execute the application to test it, press Ctrl + F5. If the browser presents a Resend button, click that button or refresh the browser
  22. In the Select Type of Account combo box, select 3. Water Intensive Business (Laundromat, etc)
  23. Click the Prepare Water Bill button

    Tuples and Classes

  24. Return to your programming environment

A Fully-Implemented Tuple Property

As seen in our introduction to properties, if you want to control the details of processing a property, you can create a private field that is the same tuple type as the property. Then add either or both a get and a set clauses. Here is an example:

@functions{
    public class Employee
    {
        private (int, string, bool) id;

        public (int, string, bool) Identification
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }
    }
}

An Automatic Tuple Property

If you are not planning to validate, accept, or reject the values of the property, you can create the property as an automatic one. Here is an example:

@functions{
    public class Employee
    {
        private (int, string, bool) id;

        public (int, string, bool) Identification
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }

        public (bool, string, PayFrequency, double) Salary { get; set; }
    }
}

When creating the property, it is a good idea, although not a requirement, to name the elements of the tuple. It may also be a good idea to add comments that explain the roles of the elements of the tuple. Here is an example:

@functions{
    public class Employee
    {
        private (int, string, bool) id;

        /* An employee is identified with three pieces of information:
         * the code is a type of employee number or contractor code,
         * the name represents the full name. It could include the 
         *          first name, the middle initial, and a last name.
         * The last piece of information indicates whether the
         *          employee is part-time (false) or full-time (true). */
        public (int code, string name, bool full_time) Identification
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }

        /* We want various pieces of information that provide details about
         * employee or contractor's salary.
         * The isFixed item indicates whether an employee can work overtime
         *             (and get paid the hourly salary and half).
         * The Pay Frequency indicates what the value of the salary
         *             represents, such as hourly, monthly, etc.
         * The last value is the actual salary. Again, that value depends 
         *             on the pay frequency. */
        public (bool isFixed, PayFrequency often, double number) Salary { get; set; }
    }
}

As mentioned in the previous lesson, if you don't name the elements of a tuple, the compiler gives them some default names as Item1, Item2, etc.

When using the property, you may need to access the elements of its type. You will access them by their names. From inside the class, such as in the body of a clause of another property or in the body of a method of the same name, type the name of the desired property, type a period, and select the element of your choice. Here are examples:

Tuples and Properties

Tuples and Properties

When using the property outside the class, if you have declared a variable of the class that owns the property, type the name of the object, a period, the name of the property, a period, and the desired element. Here is an example:

Tuples and Properties

Once you have accessed the property or any of its elements, you can use it like any property as we have done in previous lessons.

Consider a class as follows:

namespace PracticalDistribution.Pages
{
    public class Processor
    {
        /* We are combining these pieces of information of the 
         * processor because processors specifications are 
         * related by generation and tied to a manufacturer. */
        public (string make, string model, string socket) Manufacture { get; set; }

        public (int, int) Count { get; set; }
        public double Speed { get; set; }

        public Processor()
        {
            Count = (6, 12);
            Manufacture = ("AMD", "RYZEN 5", "AM4");

            Present();
        }
    }
}

If you create an automatic tuple property, you cannot individually initialize the elements of the tuple property. Still, you can access the elements to present to the user. Here is an example:

@page
@model PracticalDistribution.Pages.ExerciseModel
@{
    Processor proc = new();
}

<pre>Make:      @proc.Manufacture.make
Model:     @proc.Manufacture.model
Socket:    @proc.Manufacture.socket
Processor: @proc.Count.Item1-Core, @proc.Count.Item2-Thread.
==============================</pre>

This would produce:

Make:      AMD
Model:     RYZEN 5
Socket:    AM4
Processor: 6-Core, 12-Thread.
==============================

Topics on Tuples

Methods and Tuples

We saw how to involve tuples with functions. Everything we saw about passing a tuple as argument and returning a tuple can be applied exactly the same way to the methods of a class. Normally, methods deal with tuples exactly as we described for functions, with just minor adjustments. It is important to remember (as stated in our introductory lesson on functions) that a function is a section of code that behaves as if it written outside of a class. Otherwise, everything we studied about involving tuples and functions also applies to methods. This means that you can pass a tuple to a method and you can return a tuple from a method.

As you know already, a method is a function created inside a class. As you know already, if you create a method, it has direct access to other members of the same class. In a method, you have direct access to the names of the tuples. To access an element of a tuple, type the name of the member, a period, and the desired member. That way, you can initialize a tuple. Here is an example:

namespace PracticalDistribution.Pages
{
    public class Processor
    {
        /* We are combining these pieces of information of the 
         * processor because processors specifications are 
         * related by generation and tied to a manufacturer. */
        private (string make, string model, string socket) identification;

        private void Create()
        {
            identification = ("AMD", "RYZEN 5", "AM4");
        }
    }
}

An Object in a Tuple

All the elements we used so far in tuples were of regular types. In reality, each element is a placeholder for practically any type you want. Based on this, an element of a tuple can be an object of a structure or class type. Of course, you must have a class. You can create and use your own class. Here is an example of a class named Trapezoid created in a Windows Forms application named Geometry:

namespace PracticalDistribution.Pages
{
    public class Trapezoid
    {
        public double TopBase { get; set; }
        public double BottomBase { get; set; }
        public double Height { get; set; }

        public double Area
        {
            get
            {
                return Height * (TopBase + BottomBase) / 2.00;
            }
        }
    }
}

When creating the tuple, specify the desired element using the name of the class. Here is an example:

@page
@model PracticalDistribution.Pages.ExerciseModel
@{
    Trapezoid isosceles = new Trapezoid();
    
    (Trapezoid shape, int value) definition;
}

When initializing the tuple or when specifying its value, you must define the object used as element. You have various options:

In the above example, we used a tuple that has one element that is a class type. In the same way, you can create a tuple with more than one element that are of class or structure type. The elements can be of the same class (or structure) or different classes (or structures).

In the above example, we used our own class. On the other hand, we know that the .NET Framework provides a large collection of classes and structures. You can use any of most of the many classes of the .NET Framework for an element of a tuple.

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2001-2021, C# Key Sunday 05 November 2021 Next