Fundamentals of Records

Introduction

In previous lessons, we were introduced to classes as a way to create a layout for objects. We saw other options to create objects, such as tuples and structures. Like those options, a record is a technique to combine characteristics that describe an object. There are many similarities between classes, structures, and records. We are going to review many of those issues before getting to the differences between classes and structures on one hand, and records on the other hand.

Practical LearningPractical Learning: Introducing Records

  1. Start Microsoft Visual Studio. Create a Console App (that supports .NET 8.0 (Long-Term Support)) named TaxPreparation5
  2. To start a new file, on the main menu, click Project and click Add New Item...
  3. In the left list of the Add New Item dialog box, click Code and, in the middle list, click Code File
  4. Change the file Name to IncomeTax
  5. Click Add
  6. In the empty document, type the following lines:
    namespace TaxPreparation5
    {
    
    }

Creating a Record Type

To create a record, you use a keyword named record. The primary formula to follow is:

access-level record name {}

As always when you create a layout for a type, you can start with some options that sometimes are not required. The primary option you can apply is an access level. In many cases, the access is not required but if you decide to use one, apply public or internal to your record. The option(s) is (are) followed by the record keyword. As always, you must name the type you are creating. The name of a record follows the rules of names of classes, structures, enumerations, and namespaces. A record must have a body, which starts with an opening curly bracket { and ends with a closing curly bracket }.

Practical LearningPractical Learning: Creating a Record

Creating a Record Object

When you create a record, you are simply providing a skeleton for an eventual object. To get an actual object, declare a variable of the record. You can use the name of the record as type. This is followed by a name. The name of the object follows the rules of names of variables. Add the assignment operator. Initialize the object with the new operator. Add some parentheses and end the declaration with a semicolon. Here is an example:

IncomeTax it = new();

internal record IncomeTax
{
}

As see with classes, there are various was you can initialize an object of a record type. We have seen that you can use the name of the record to create an object. An alternative is to use the var keyword. In this case, you must add the name of the class between new and the parentheses. Here is an example:

var it = new IncomeTax();

internal record IncomeTax
{
}

In the same way, instead of var, you can use the dynamic keyword. As one more option, even if you declare the variable using the name of the class, you can still add the name of the class between new and the parentheses. Here is an example:

IncomeTax it = new IncomeTax();

internal record IncomeTax
{
}

Practical LearningPractical Learning: Accessing a Record

  1. In the Solution Explorer, right-click Program and click Rename
  2. Type TaxPreparation (to get TaxPreparation.cs) and press Enter
  3. Change the TaxPreparation.cs document as follows:
    using static System.Console;
    using TaxPreparation5;
    
    IncomeTax it = new IncomeTax();
  4. To execute, on the main menu, click Debug and click Start Without Debugging
  5. Close the window and return to your programming environment

A Field in a Record

When creating a record, you should fill it with some useful members. As seen with classes and structures, the simplest thing you can do in a record is to declare a variable of a primitive type. Such a variable is also called a field. Here is an example:

internal record Employee
{
    string nbr;
}

You can then use that field. Before doing that, you should apply an appropriate access level. As a reminder, if you apply the private level, the field can be accessed only by members of the same record. If you want the field to be accessed outside the record, you should apply the internal (to access its objects only in the project) or the public (to be able to access the record outside the project) keyword.

The Properties of a Record

A property is a characteristic that allows a record to communicate or exchange values with other objects. As seen with classes and structures, all options to create properties are available in a record. As a reminder, if you had added a private field to a record, you can create a complete property for it, with a get and a set clauses. Here is an example:

internal record Employee
{
    int nbr;

    public int EmployeeNumber
    {
        get
        {
            return nbr;
        }

        set
        {
            nbr = value;
        }
    }
}

After creating a property, you can access it the same way you would access a property of a class or structure. Here is an example:

Employee staff = new Employee();

staff.EmployeeNumber = 428_350;

Console.WriteLine("Employee #: {0}", staff.EmployeeNumber);

internal record Employee
{
    int nbr;

    public int EmployeeNumber
    {
        get
        {
            return nbr;
        }

        set
        {
            nbr = value;
        }
    }
}

This would produce:

Employee #: 428350

Press any key to close this window . . .

In the same way, you can use a conditional statement in a clause to accept a value, change a value, or reject a value. Here is an example:

Employee staff = new Employee();

staff.EmployeeNumber = 428_350;
staff.HourlySalary = 8.95;

Console.WriteLine("Employee #:    {0}", staff.EmployeeNumber);
Console.WriteLine("Hourly Salary: {0}", staff.HourlySalary);

internal record Employee
{
    int nbr;
    double sal;

    public int EmployeeNumber
    {
        get { return nbr; }
        set { nbr = value; }
    }

    internal double HourlySalary
    {
        get
        {
            if (sal <= 10.00)
                return 15.55;
            else
                return sal;
        }

        set
        {
            sal = value;
        }
    }
}

This would produce:

Employee #:    428350
Hourly Salary: 15.55

Press any key to close this window . . .

In the same way, you can create an automatic property in a record. Here are examples:

internal record 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; }
}

You can create an automatic property in a record

An Enumerated Property

As seen with structures and classes, a field of a record can be an enumerated type. Here is an example:

public enum FilingStatus { Single, Married, Unknown }

internal record Employee
{
    FilingStatus category;
}

In the same way, you can create a property of an enumeration type. Here is an example:

public enum FilingStatus { Single, Married, Unknown }

internal record Employee
{
    private FilingStatus category;

    public FilingStatus FilingStatus
    {
	get
	{
	    return category;
	}

	set
	{
	    category = value;
	}
    }
}

Of course, you can create the property as an automatic one.

The Methods of a Record

Like a class or a structure, a record can have methods. You create the method exactly the same way you would create a method in a class or structure. Here is an example:

public enum FilingStatus { Single, Married, Unknown }

internal record Employee
{
    private FilingStatus category;

    public FilingStatus FilingStatus
    {
        get
        {
            return category;
        }

        set
        {
            category = value;
        }
    }

    public string? FirstName { get; set; }
    public string? LastName  { get; set; }

    public string GetFullName()
    {
        if (LastName == "")
            throw new System.Exception("The last name is required for each employee.");

        return FirstName + " " + LastName;
    }
}

Everything else we reviewed for methods is available for records. For example, you can overload a method by passing different arguments to methods that use the same name. Here are examples:

public enum WorkCategory { Employee, Manager, Contractor, Seasonal, Unknown }

internal record StoreItem
{
    public string? PasswordManagement { get; set; }
    public string? HiringSupervision  { get; set; }
    public string? Description        { get; set; }

    // A method with a Boolean parameter
    public void Validate(bool isvalid)
    {
        if(isvalid)
            PasswordManagement = "Can Change Account Password";
        else
            PasswordManagement = "Regular Employee";
    }

    // A method with an enumerated parameter
    public void Validate(WorkCategory level)
    {
        if (level == WorkCategory.Manager)
            HiringSupervision = "Can Approve Hiring";
        else
            HiringSupervision = "Regular Employee";
    }

    // A method with two parameters
    public void Validate(int type, WorkCategory level)
    {
        switch (type)
        {
            case 2 when level == WorkCategory.Contractor:
                Description = "The job is a contract position on a temporary basis.";
                break;
            case 1 when level == WorkCategory.Seasonal:
                Description = "This is a full-time seasonal employee hired when season is at high pick.";
                break;
            case 3 when level == WorkCategory.Employee:
                Description = "The employment is be determined.";
                break;
            case 1 when level == WorkCategory.Manager:
                Description = "This is a managerial position with corporate credit cards.";
                break;
            case 2 when level == WorkCategory.Employee:
                Description = "This is a part-time employment position but put a regular payroll.";
                break;
        }
    }
}

Records and Functions

Returning an Object of a Record Type

Once you have created a record, it becomes a regular type. You can involve it with a function. For example, you can create a function that returns a record object. To start, create a function or method whose return type is an existing record. In the body of the function or method, process the values any way you like. Before the closing curly bracket of the function or method, you must return an object that holds a record object. Here is an example:

using static System.Console;

public class TaxPreparation
{
    Employee Create()
    {
        WriteLine("Enter the information about the employee");
        Write("Employee #:  ");
        int number = int.Parse(ReadLine()!);
        Write("Frist Name:  ");
        string fName = ReadLine()!;
        Write("Last Name:   ");
        string lName = ReadLine()!;
        Write("Hourly Rate: ");
        double salary = double.Parse(ReadLine()!);

        return new Employee()
        {
            EmployeeNumber = number,
            FirstName = fName,
            LastName = lName,
            HourlySalary = salary
        };
    }
}

internal record Employee
{
    public int     EmployeeNumber { get; set; }
    public string? FirstName      { get; set; }
    public string? LastName       { get; set; }
    public double  HourlySalary   { get; set; }
}

An Argumented Record

Since a record is a type, you can create a parameter of that type. To do that, when creating the function or method, specify the type of a parameter as a record type. In the body of the function or method, use or ignore the parameter. When calling the function or method, make sure you pass an argument that holds a record object. Here is an example:

using static System.Console;

Employee staff = Create();

Show(staff);

Employee Create()
{
    WriteLine("Enter the information about the employee");
    Write("Employee #:  ");
    int number = int.Parse(ReadLine()!);
    Write("Frist Name:  ");
    string fName = ReadLine()!;
    Write("Last Name:   ");
    string lName = ReadLine()!;
    Write("Hourly Rate: ");
    double salary = double.Parse(ReadLine()!);

    return new Employee()
    {
        EmployeeNumber = number,
        FirstName = fName,
        LastName = lName,
        HourlySalary = salary
    };
}

void Show(Employee staff)
{
    WriteLine("=========================================");
    WriteLine("  - Human Resources -");
    WriteLine("-----------------------------------------");
    WriteLine("-=- Employee Detials -=-");
    WriteLine("=========================================");
    WriteLine($"Employee #:   {staff.EmployeeNumber}");
    WriteLine($"First Name:   {staff.FirstName}");
    WriteLine($"Last Name:    {staff.LastName}");
    WriteLine($"Gross Salary: {staff.FirstName}");
    WriteLine($"Last Name:    {staff.HourlySalary:f}");
    WriteLine("=========================================");
}

internal record Employee
{
    public int     EmployeeNumber { get; set; }
    public string? FirstName      { get; set; }
    public string? LastName       { get; set; }
    public double  HourlySalary   { get; set; }
}

Here is an example of running the program:

Enter the information about the employee
Employee #:  913740
Frist Name:  Jennifer
Last Name:   Beals
Hourly Rate: 31.25
============================================
 - Human Resources -
--------------------------------------------
          -=- Employee Detials -=-
============================================
Employee #:   913740
First Name:   Jennifer
Last Name:    Beals
Gross Salary: Jennifer
Last Name:    31.25
============================================

Press any key to close this window . . .

Records and Records

A Record as a Type

A record can be involved with other records as normally as a type can be. For example, a property of a record can be a record type. Here is an example:

public record Identification
{
    public string? Abbreviation { get; set; }
    public string? GeneralName  { get; set; }
    public string? ISOName      { get; set; }
}

public record State
{
    public Identification Identification { get; set; }
    public string? Capital                { get; set; }
    public long   Area                   { get; set; }
    public long   Population             { get; set; }
}

Records and Inheritance

Records support inheritance, but a record can be derived only from another record. This means that you must first have a record that would serve as parent. Here is an example:

public record Employee
{
    public int    EmployeeNumber { get; set; }
    public string? FirstName      { get; set; }
    public string? LastName       { get; set; }
    public double HourlySalary   { get; set; }
}

public record Manager : Employee
{
    public string? JobTitle         { get; set; }
    public bool   CanUnlockAccount { get; set; }
}

Inheriting from Object

You cannot derive a record from a class. As a result, the following code will produce an error:

public class Vehicle
{
    public int Doors { get; set; }
}

public record Tractor : Vehicle
{
    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; }
}

This error is because you cannot create a record that is based on a class. There is an exception. Remember that every time you create a class, the class is automatically derived directly or indirectly from the object type. We saw that this concept also applies to structures. It also applies to records. In fact, when you create a record, you can explictly indicate that it derives from the object type. Here is an example:

Title = "Tractor Characteristics";

Tractor machine = new Tractor();

machine.ModelName    = "MW9724";
machine.EnginePower  = 21.5f;
machine.LiftCapacity = 754;
machine.MowerHeight  = 60;
machine.Price        = 13_050;

Console.WriteLine("Tractor Characteristics");
Console.WriteLine("=============================");
Console.WriteLine($"Model Name:     {machine.ModelName}");
Console.WriteLine($"Engine Power:   {machine.EnginePower} hp");
Console.WriteLine($"Lift Capacity:  {machine.ModelName} lbs");
Console.WriteLine($"Mower Height:   {machine.MowerHeight}");
Console.WriteLine($"Price:          {machine.Price:C}");
Console.WriteLine("=============================");

public record Tractor : object
{
    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; }
}

This would produce::

Tractor Characteristics
=============================
Model Name:     MW9724
Engine Power:   21.5 hp
Lift Capacity:  MW9724 lbs
Mower Height:   60
Price:          $13,050.00
=============================

Press any key to close this window . . .

Probably the primary characteristic of the object class is that ot provides the primary features, mainly methods that all classes would need. One of these methods is the abiliby to find out whether two objects are equal. To make this happen, when you create a class, you can/must override the Equals() method. When it comes to records, the ability to compare two records objects is built in the record concept. Therefore, if you create a record, you cannot (you are not allowed to) override the Equals() method. If you do, you would receive an error.

Records and Classes

A Member of a Class Type

A member of a record, as a field or property, can be of the type of a class. You can use a .NET built-in class or you can create your own class. Then, in the body of a record, create a member whose type is a class. Here is an example:

public enum ChargeStatus { Negative, Neutral, Positive, Unknown }

// A class type
public class Nucleus
{
    // A nucleus is made of one or more protons and neutrons
    public int Protons  { get; set; }
    public int Neutrons { get; set; }
}

// A record layout
public record Atom
{
    // An atom is made of a nucleus (and one or more electrons)
    public Nucleus Nucleus { get; set; }
    public int Electrons   { get; set; }
    public ChargeStatus ChargeStatus { get; set; }
}

A Record for a Class Member

The type of a member of a class, such as a field or a property, can be based on a record. When creating the member, simply specify its type as an existing record. You can then use the member as regularly as necessary.

Practical LearningPractical Learning: Introducing the Size Mode of a Picture Box

  1. Click the IncomeTax.cs tab to access its file and change the document as follows:
    namespace TaxPreparation5
    {
        public enum FilingStatus { Single, Married, Unknown }
    
        internal record Employee
        {
            public int    EmployeeNumber { get; set; }
            public string? FirstName      { get; set; }
            public string? LastName       { get; set; }
            public FilingStatus FilingStatus { get; set; }
        }
    
        internal record IncomeTax
        {
            public int      TaxIdentifier { get; set; }
            public Employee Employee      { get; set; } = new Employee();
            public double   GrossSalary   { get; set; }
    
            public double TaxAmount
            {
                get
                {
                    double amount = 0.00;
    
                    switch (Employee.FilingStatus)
                    {
                        case FilingStatus.Single:
                            if( (GrossSalary >= 0.00) && (GrossSalary <= 135.00))
                            {
                                amount = 0.00;
                            }
                            else if ((GrossSalary > 135.00) && (GrossSalary <= 712.00))
                            {
                                amount = (GrossSalary - 135.00) * 3.10 / 100.00;
                            }
                            else if ((GrossSalary > 712.00) && (GrossSalary <= 1288.00))
                            {
                                amount = ((GrossSalary - 712.00) * 5.25 / 100.00) + 17.88;
                            }
                            else // if (GrossSalary > 1288.00)
                            {
                                amount = ((GrossSalary - 1288.00) * 5.70 / 100.00) + 48.17;
                            }
                            break;
    
                        case FilingStatus.Married:
                            if ((GrossSalary >= 0.00) && (GrossSalary <= 308.00))
                            {
                                amount = 0.00;
                            }
                            else if ((GrossSalary > 308.00) && (GrossSalary <= 1462.00))
                            {
                                amount = (GrossSalary - 308.00) * 3.10 / 100.00;
                            }
                            else if ((GrossSalary > 1462.00) && (GrossSalary <= 2615.00))
                            {
                                amount = ((GrossSalary - 1462.00) * 5.25 / 100.00) + 35.77;
                            }
                            else // if (GrossSalary > 2615.00)
                            {
                                amount = ((GrossSalary - 2615.00) * 5.70 / 100.00) + 96.35;
                            }
                            break;
    
                        default:
                            amount = 0.00;
                            break;
                    }
    
                    return amount;
                }
            }
    
            public double NetSalary
            {
                get
                {
                    return GrossSalary - TaxAmount;
                }
            }
        }
    }
  2. Click the TaxPreparation.cs tab to access its file and change the document as follows:
    using static System.Console;
    using TaxPreparation5;
    
    IncomeTax Create()
    {
        Random rndNumber = new Random();
        IncomeTax income = new IncomeTax();
    
        WriteLine("==========================================");
        WriteLine(" - Amazing DeltaX - State Income Tax -");
        WriteLine("------------------------------------------");
        WriteLine("          -=- Kansas -=-");
        WriteLine("==========================================");
        WriteLine("Enter the information about the employee");
        income.Employee.EmployeeNumber = rndNumber.Next(100001, 999999);
        
        Write("Frist Name:             ");
        income.Employee.FirstName = ReadLine()!;
        Write("Last Name:              ");
        income.Employee.LastName = ReadLine()!;
        WriteLine("------------------------------------------");
        WriteLine("Options on Filing Status");
        WriteLine("1 - Single Person");
        WriteLine("2 - Married Person");
        Write("Type the filing status: ");
        string selection = ReadLine()!;
        WriteLine("------------------------------------------");
    
        if (selection == "1")
        {
            income.Employee.FilingStatus = FilingStatus.Single;
        }
        else if (selection == "2")
        {
            income.Employee.FilingStatus = FilingStatus.Married;
        }
        else
            income.Employee.FilingStatus = FilingStatus.Unknown;
    
        double salary = 0.00;
    
        try
        {
            Write("Gross Salary:           ");
            salary = double.Parse(ReadLine()!);
        }
        catch(FormatException exc)
        {
            WriteLine("It looks like you typed an invalid value for the gross income. The error produced is " + exc.Message);
        }
    
        income.TaxIdentifier = 1001;
        income.GrossSalary = salary;
    
        return income;
    }
    
    void Show(IncomeTax tax)
    {
        WriteLine("==========================================");
        WriteLine(" - Amazing DeltaX - State Income Tax -");
        WriteLine("------------------------------------------");
        WriteLine("          -=- Kansas -=-");
        WriteLine("==========================================");
        WriteLine($"Tax Identifier:  {tax.TaxIdentifier}");
        WriteLine("------------------------------------------");
        WriteLine($"Employee #:      {tax.Employee.EmployeeNumber}");
        WriteLine($"First Name:      {tax.Employee.FirstName}");
        WriteLine($"Last Name:       {tax.Employee.LastName}");
        WriteLine($"Filing Status:   {tax.Employee.FilingStatus}");
        WriteLine("------------------------------------------");
        WriteLine($"Gross Salary:    {tax.GrossSalary:f}");
        WriteLine($"Tax Amount:      {tax.TaxAmount:f}");
        WriteLine($"Net Pay:         {tax.NetSalary:f}");
        WriteLine("==========================================");
    }
    
    IncomeTax it = Create();
    
    Clear();
    
    Show(it);
  3. To execute the application, press Ctrl + F5
  4. When requested, type the values as followed and press Enter after each value:
    First Name: Vanessa
    Last Name: Swanson
    Filing Status: 1
    Gross Salary:  1648.83
    Here is the result:
    ==========================================
     - Amazing DeltaX - State Income Tax -
    ------------------------------------------
              -=- Kansas -=-
    ==========================================
    Tax Identifier:  1001
    ------------------------------------------
    Employee #:      610119
    First Name:      Vanessa
    Last Name:       Swanson
    Filing Status:   Single
    ------------------------------------------
    Gross Salary:    1648.83
    Tax Amount:      68.74
    Net Pay:         1580.09
    ==========================================
    
    Press any key to close this window . . .
  5. To close the window, press Q, and return to your programming environment

Records and Constructors

Introduction

Both the classes and the structures support constructors, as we saw already. Constructors are a little different and particularly important in records.

Like classes and structures, a record can have constructors. In fact, if you create a record without a constructor, the compiler would create a default constructor for you. Still, if you want, you can create a default constructor in your record. Here is an example:

public record VehicleRegistration
{
    public VehicleRegistration()
    {
    }
}

Of course, when you use the default constructor to instantiate a record object, you can use the curly brackets to make a list of the properties of the record and provide a value for each. Here is an example:

VehicleRegistration vr = new VehicleRegistration()
{
    TagNumber = "HPR-385",
    Make = "Ford",
    Model = "Escape",
    Year = 2020
};

Console.Title = "Vehicle Registration";

Console.WriteLine("Vehicle Registration");
Console.WriteLine("=====================");
Console.WriteLine($"Tag #: {vr.TagNumber}");
Console.WriteLine($"Make:  {vr.Make}");
Console.WriteLine($"Model: {vr.Model}");
Console.WriteLine($"Year:  {vr.Year}");
Console.WriteLine("=====================");
        
public record VehicleRegistration
{
    int yr;
    string? _nbr;
    string? _make, _model;

    public VehicleRegistration()
    {
    }

    public string? TagNumber
    {
        get
        {
            return _nbr;
        }

        set
        {
            _nbr = value;
        }
    }

    public string? Make
    {
        get { return _make;  }
        set { _make = value; }
    }

    public string? Model
    {
        get { return _model; }
        set { _model = value; }
    }

    public int Year
    {
        get { return yr;  }
        set { yr = value; }
    }
}

public class Exercise
{
    static int Main()
    {
        

        return 54;
    }
}

This would produce:

Vehicle Registration
=====================
Tag #: HPR-385
Make:  Ford
Model: Escape
Year:  2020
=====================

Press any key to close this window . . .

A Parameterized Constructor

If you judge it necessary, when creating a record, you can create a constructor that takes at least one parameter. Here is an example:

public record VehicleRegistration
{
    string? _nbr;

    public VehicleRegistration(string number)
    {
    }
}

You can then use such a constructor to create an object of the record. Here is an example:

VehicleRegistration vr = new VehicleRegistration("HPR-385");

vr.Make = "Ford";
vr.Model = "Escape";
vr.Year = 2020;

Console.Title = "Vehicle Registration";

Console.WriteLine("Vehicle Registration");
Console.WriteLine("=====================");
Console.WriteLine($"Tag #: {vr.TagNumber}");
Console.WriteLine($"Make:  {vr.Make}");
Console.WriteLine($"Model: {vr.Model}");
Console.WriteLine($"Year:  {vr.Year}");
Console.WriteLine("=====================");

public record VehicleRegistration
{
    int yr;
    string? _nbr;
    string? _make, _model;

    public VehicleRegistration(string number)
    {
        _nbr = number;
    }

    . . .
}

As mentioned with classes, if you create a constructor in a record and that constructor takes at least one parameter, the default constructor disappears (remember that this is different for structures: a structure always has a default constructor, even if you create at least one constructor that takes at least one parameter). The solution is to manually create various constructors because, as we saw with methods, records support method overloading. To do this, create constructors that take different numbers or different types of parameters. You can then call each constructor as needed. Here are examples:

// Using the default constructor to create an object
VehicleRegistration vr = new VehicleRegistration()
{
    TagNumber = "HPR-385",
    Make      = "Ford",
    Model     = "Escape",
    Year      = 2020
};

// Using a constructor with one parameter
VehicleRegistration vehicle = new VehicleRegistration("937052")
{
    Make  = "Toyota",
    Model = "Corolla",
    Year  = 1998
};

// A constructor with two parameters
VehicleRegistration register = new VehicleRegistration("Buick", "LeSabre")
{
    TagNumber = "FKE-RLW",
    Year      = 2020
};

// A public constructor
VehicleRegistration registration = new VehicleRegistration("GH4-G88", "Lincoln", "Continental", 2006);

public record VehicleRegistration
{
    int yr;
    string? _nbr;
    string? _make, _model;

    public VehicleRegistration()
    {
        _nbr   = "000-000";
        _make  = "Collectible";
        _model = "Classic";
        yr     = 1960;
    }

    public VehicleRegistration(string number)
    {
        _nbr = number;
    }

    public VehicleRegistration(string make, string model)
    {
        _make  = make;
        _model = model;
    }

    public VehicleRegistration(string nbr, string make, string model, int year)
    {
        _nbr   = nbr;
        _make  = make;
        _model = model;
        yr     = year;
    }

    public string? TagNumber
    {
        get { return _nbr; }
        set { _nbr = value; }
    }

    public string? Make
    {
        get { return _make;  }
        set { _make = value; }
    }

    public string? Model
    {
        get { return _model; }
        set { _model = value; }
    }

    public int Year
    {
        get { return yr;  }
        set { yr = value; }
    }
}

Initializing a Property

We know that you can create a property with a set clause, in which case you can let the clients of the record assign a desired value to the property after an object of the record has been created. Here are examples of such properties:

Canoe fun = new Canoe();

fun.Identification = 280_385;
fun.Make           = "Emotion";
fun.Model          = "Wasatch ";
fun.Length         = 13;
fun.Price          = 479.99;
        
Console.Title = "Canoe Registration";

Console.WriteLine("Canoe Registration");
Console.WriteLine("=====================");
Console.WriteLine($"Identifier: {fun.Identification}");
Console.WriteLine("---------------------");
Console.WriteLine($"Make:       {fun.Make}");
Console.WriteLine($"Model:      {fun.Model}");
Console.WriteLine($"Length:     {fun.Length}");
Console.WriteLine($"Price:      {fun.Price}");
Console.WriteLine("=====================");

internal record Canoe
{
    int id;
    int len;
    double cost;
    string? _make, _model;

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

    public string? Make
    {
        get { return _make;  }
        set { _make = value; }
    }

    public string? Model
    {
        get { return _model; }
        set { _model = value; }
    }

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

    public double Price
    {
        get { return cost; }
        set { cost = value; }
    }
}

This would produce:

Canoe Registration
=====================
Identifier: 280385
---------------------
Make:       Emotion
Model:      Wasatch
Length:     13
Price:      479.99
=====================

Press any key to close this window . . .

If you want to require that a certain property be given a value immediately when a record object is created, you must create a clause named init instead of a set clause. Here is an example:

internal record Canoe
{
    int id;

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

After doing that, the property must receive a value immediately when the record is instantiated. To make this happen, you have two options. When you create an object of the record, you can use the curly brackets to initialize the object. This can be done as follows:

Canoe fun = new Canoe()
{
    Make           = "Emotion",
    Model          = "Wasatch ",
    Identification = 280_385,
    Length         = 13,
    Price          = 479.99
};
        
Console.Title = "Canoe Registration";

Console.WriteLine("Canoe Registration");
Console.WriteLine("=====================");
Console.WriteLine($"Identifier: {fun.Identification}");
Console.WriteLine("---------------------");
Console.WriteLine($"Make:       {fun.Make}");
Console.WriteLine($"Model:      {fun.Model}");
Console.WriteLine($"Length:     {fun.Length}");
Console.WriteLine($"Price:      {fun.Price}");
Console.WriteLine("=====================");
        
internal record Canoe
{
    int id;
    int len;
    double cost;
    string? _make, _model;

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

    public string? Make
    {
        get { return _make;  }
        set { _make = value; }
    }

    public string? Model
    {
        get { return _model; }
        set { _model = value; }
    }

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

    public double Price
    {
        get { return cost; }
        set { cost = value; }
    }
}

An alternate solution is to create a constructor in the record. That constructor must have a parameter for he property. In the body of the constructor, assign the parameter to the property or to its associated field. This can be done as follows:

Canoe fun  = new Canoe(280_385);

fun.Make   = "Emotion";
fun.Model  = "Wasatch ";
fun.Length = 13;
fun.Price  = 479.99;
        
Console.Title = "Canoe Registration";

Console.WriteLine("Canoe Registration");
Console.WriteLine("=====================");
Console.WriteLine($"Identifier: {fun.Identification}");
Console.WriteLine("---------------------");
Console.WriteLine($"Make:       {fun.Make}");
Console.WriteLine($"Model:      {fun.Model}");
Console.WriteLine($"Length:     {fun.Length}");
Console.WriteLine($"Price:      {fun.Price}");
Console.WriteLine("=====================");

internal record Canoe
{
    int id;
    int len;
    double cost;
    string? _make, _model;

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

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

    public string? Make
    {
        get { return _make;  }
        set { _make = value; }
    }

    public string? Model
    {
        get { return _model; }
        set { _model = value; }
    }

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

    public double Price
    {
        get { return cost; }
        set { cost = value; }
    }
}

In the same way, in a record, you can create various properties that have init clauses. You can have a mix of properties with set and init clauses. You can also create automatic properties that usse init clauses. Remember that, when you are creating an object of the record, you must use one of both options we reviewed to initialize an caption property. Here is an example:

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

fun.Length = 13;
fun.Price  = 479.99;
        
Console.Title = "Canoe Registration";

Console.WriteLine("Canoe Registration");
Console.WriteLine("=====================");
Console.WriteLine($"Identifier: {fun.Identification}");
Console.WriteLine("---------------------");
Console.WriteLine($"Make:       {fun.Make}");
Console.WriteLine($"Model:      {fun.Model}");
Console.WriteLine($"Length:     {fun.Length}");
Console.WriteLine($"Price:      {fun.Price}");
Console.WriteLine("=====================");

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

    public int    Identification { get; init; }
    public string? Make           { get; init; }
    public string? Model          { get; init; }
    public int    Length         { get; set; }
    public double Price          { get; set; }
}

Practical LearningPractical Learning: Ending the Lesson


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