Structures and Value Types

Structures and Tuples

We already know that a tuple is a technique to combine types and treat them as one type. The types must be included in parentheses and they are separated with comas.

Probably the most basic way to use a tuple is when initializing a group of values in a constructor. Here is an example:

using static System.Console;

Machine mach = new Machine(239_740, "Bernette", "b33", 255.55);

WriteLine("Sewing Others");
WriteLine("===========================");
WriteLine("Machine Characteristics");
WriteLine("---------------------------");
WriteLine("Item Code:   {0}", mach.ItemCode);
WriteLine("Make:        {0}", mach.Make);
WriteLine("Model:       {0}", mach.Model);
WriteLine("Price:       ${0}", mach.UnitPrice);
WriteLine("===========================");

internal readonly struct Machine
{
    internal int ItemCode
    {
        get;
        init;
    }

    internal string? Make
    {
        init;
        get;
    }

    internal string? Model
    {
        get;
        init;
    }

    public double UnitPrice
    {
        init;
        get;
    }

    internal Machine(int code, string? make, string? model, double price)
    {
        (ItemCode, Make, Model, UnitPrice) = (code, make, model, price);
    }
}

This would produce:

Sewing Others
===========================
Machine Characteristics
---------------------------
Item Code:    239740
Make:         Bernette
Model:        b33
Price:       $255.55
===========================

Press any key to close this window . . .

Tuples are involved with structure the exact same ways we saw with classes. This means that, in a structure, you can create a field that is a tuple type. Here is an example:

internal struct Machine
{
    (int width, int height, int depth) _dims;
}

In a structure, you can create a property that is a tuple type. You can create a read/write property that is completely defined. Here is an example:

using static System.Console;

Machine mach = new Machine(239_740, "Bernette", "b33", 255.55);

mach.Dimensions = (16, 14, 8);

WriteLine("Sewing Others");
WriteLine("===========================================================");
WriteLine("Machine Characteristics");
WriteLine("-----------------------------------------------------------");
WriteLine("Item Code:                {0}", mach.ItemCode);
WriteLine("Make:                     {0}", mach.Make);
WriteLine("Model:                    {0}", mach.Model);
WriteLine("Dimensions (W x H x D):   {0} inches x {1} inches x {2} inches",
           mach.Dimensions.width, mach.Dimensions.height, mach.Dimensions.depth);
WriteLine("Price:                   ${0}", mach.UnitPrice);
WriteLine("===========================================================");

internal struct Machine
{
    (int width, int height, int depth) _dims;

    internal int ItemCode
    {
        get;
        init;
    }

    internal string? Make
    {
        init;
        get;
    }

    internal string? Model
    {
        get;
        init;
    }

    public double UnitPrice
    {
        init;
        get;
    }

    internal (int width, int height, int depth) Dimensions
    {
        get
        {
            return _dims;
        }
        set
        {
            _dims = value;
        }
    }

    internal Machine(int code, string? make, string? model, double price)
    {
        (ItemCode, Make, Model, UnitPrice) = (code, make, model, price);
    }
}

Of course, you can also create an automatic property of a tuple type. You can also make the property read-only. If you create a read-only structure, make sure you provide a way for the user to initialize the tuple property. Here is an example:

using static System.Console;

Machine mach = new Machine(239_740, "Bernette", "b33", (16, 14, 8), 255.55);

WriteLine("Sewing Others");
WriteLine("===========================================================");
WriteLine("Machine Characteristics");
WriteLine("-----------------------------------------------------------");
WriteLine("Item Code:                {0}", mach.ItemCode);
WriteLine("Make:                     {0}", mach.Make);
WriteLine("Model:                    {0}", mach.Model);
WriteLine("Dimensions (W x H x D):   {0} inches x {1} inches x {2} inches",
           mach.Dimensions.width, mach.Dimensions.height, mach.Dimensions.depth);
WriteLine("Price:                   ${0}", mach.UnitPrice);
WriteLine("===========================================================");

internal readonly struct Machine
{
    internal int ItemCode
    {
        get;
        init;
    }

    internal string? Make
    {
        init;
        get;
    }

    internal string? Model
    {
        get;
        init;
    }

    public double UnitPrice
    {
        init;
        get;
    }

    internal (int width, int height, int depth) Dimensions
    {
        get;
        init;
    }

    internal Machine(int code,
                     string? make,
                     string? model,
                    (int width, int height, int depth) dim,
                     double price)
    {
        (ItemCode, Make, Model, Dimensions, UnitPrice) = (code, make, model, dim, price);
    }
}

This would produce:

Sewing Others
===========================================================
Machine Characteristics
-----------------------------------------------------------
Item Code:                239740
Make:                     Bernette
Model:                    b33
Dimensions (W x H x D):   16 inches x 14 inches x 8 inches
Price:                   $255.55
===========================================================

Press any key to close this window . . .

Practical LearningPractical Learning: Introducing Topics on Structures

  1. Start Microsoft Visual Studio. Create a new Console App named RoadSystemDatabase2 that uses the .NET 8.0 (Long-Term Support)
  2. In the Solution Explorer, click the RoadSystemDatabase2 project -> Add -> Folder
  3. Type Models>

Structures and Enumerations

An enumeration can be involved with a structure. The typical way is to create a field that is of an enumeration type. Here is an example:

public enum Category { Highway, Interstate }

internal struct Road
{
    Category cat;
}

In the same way, you can create a property of the type of an enumeration. You can then use the property like we learned for classes.

Practical LearningPractical Learning: Using an Enumeration

  1. In the Solution Explorer, right-click Models -> Add -> Class...
  2. Change the name to Category
  3. Click Add
  4. To create an enumeration, change the document as follows:
    namespace RoadSystemDatabase1
    {
        internal enum Category
        {
            Regular,
            StateHighway,
            USHighway,
            Interstate,
            Beltway
        }
    }
  5. In the Solution Explorer, right-click Models -> Add -> Class...
  6. Change the name to Road
  7. Press Enter
  8. Change the class as follows:
    namespace RoadSystemDatabase1
    {
        internal struct Road
        {
            Category cat;
            private string? id;
            private double len;
            private string? ending;
            private string? beginning;
    
            public Road()
            {
                len       = 0.00;
                id        = "";
                ending    = "";
                beginning = "";
                cat       = Category.Regular;
            }
    
            public Road(string identification)
            {
                len       = 0.00;
                ending    = "";
                beginning = "";
                id        = identification;
                cat = Category.Regular;
            }
    
            public Road(string identification, double length)
            {
                ending    = "";
                len       = length;
                beginning = "";
                id        = identification;
                cat = Category.Regular;
            }
    
            public string? Designation
            {
                get
                {
                    return id;
                }
                set
                {
                    id = value;
                }
            }
    
            public Category RoadType
            {
                get { return cat;  }
                set { cat = value; }
            }
    
            public double Distance
            {
                get { return len; }
                set { len = value; }
            }
    
            public string? Start
            {
                get { return beginning; }
                set { beginning = value; }
            }
    
            public string? End
            {
                get { return ending; }
                set { ending = value; }
            }
    
            public double GetDistanceInKilometers()
            {
                return Distance * 1.6093;
            }
        }
    }
  9. In the Solution Explorer, right-click Program.cs and click Rename
  10. Type RoadSystemDatabase (to get RoadSystemDatabase.cs) and press Enter twice
  11. Change the document as follows:
    using static System.Console;
    
    using RoadSystemDatabase1
    
    Road rd = new Road();
    rd.Designation = "I-83";
    
    // Roads Categories: Regular, Beltway, Interstate, US Highway, State Highway
    rd.RoadType = Category.Interstate;
    
    rd.Distance = 85.3;
    rd.End = "I-81 / US 322 / Capital Beltway in Harrisburg, PA";
    rd.Start = "East Fayette Street / North President Street / Fallsway in Baltimore, MD";
    
    WriteLine("Road System Database");
    WriteLine("=====================================================================================");
    WriteLine("Road Name: {0}", rd.Designation);
    
    WriteLine("Road Type: {0}", rd.RoadType);
    
    WriteLine("Start:     {0}", rd.Start);
    WriteLine("End:       {0}", rd.End);
    WriteLine("Length:    {0} miles, {1} kilometers", rd.Distance, rd.GetDistanceInKilometers());
    WriteLine("=====================================================================================");
  12. To execute the project, press Ctrl + F5
    Road System Database
    =====================================================================================
    Road Name: I-83
    Start:     East Fayette Street / North President Street / Fallsway in Baltimore, MD
    End:       I-81 / US 322 / Capital Beltway in Harrisburg, PA
    Road Type: Interstate
    Length:    85.3  miles, 137.27329 kilometers
    =====================================================================================
    
    Press any key to close this window . . .
  13. To close the window and return to your programming environment, press E

Structures and Functions or Methods

A Parameter of a Structure Type

You can create a parameter of a function or method and make that parameter a structure type. When calling the function or method, make sure you pass an appropriate object. Here is an example:

using static System.Console;

Machine mach = new Machine(239_740, "Bernette", "b33", (16, 14, 8), 255.55);

// Calling the function
Present(mach);

// Creating a parameter of a structure type
void Present(Machine machine)
{
    WriteLine("Sewing Others");
    WriteLine("===========================================================");
    WriteLine("Machine Characteristics");
    WriteLine("-----------------------------------------------------------");
    WriteLine("Item Code:                {0}", machine.ItemCode);
    WriteLine("Make:                     {0}", machine.Make);
    WriteLine("Model:                    {0}", machine.Model);
    WriteLine("Dimensions (W x H x D):   {0} inches x {1} inches x {2} inches",
               machine.Dimensions.width, machine.Dimensions.height, machine.Dimensions.depth);
    WriteLine("Price:                   ${0}", machine.UnitPrice);
}

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

internal ref struct Machine
{
    internal int ItemCode
    {
        get;
        init;
    }

    internal string? Make
    {
        init;
        get;
    }

    internal string? Model
    {
        get;
        init;
    }

    public double UnitPrice
    {
        init;
        get;
    }

    internal (int width, int height, int depth) Dimensions
    {
        get;
        init;
    }

    internal Machine(int code,
                     string? make,
                     string? model,
                    (int width, int height, int depth) dim,
                     double price)
    {
        (ItemCode, Make, Model, Dimensions, UnitPrice) = (code, make, model, dim, price);
    }
}

Returning a Structured Object from a Function or Method

Like a regular data type or a class, a structure can serve as the return type of a function or a method. The rules are more related to those of a class. When creating the method, type the name of the structure on the left side of the name of the method. In the body of the method, implement the desired behavior. Before exiting the method, make sure you return a valid value that is of the type of the structure.

When a method returns a value of the type of a structure, you can assign the method call to a variable of the type of the structure. Here is an example:

using static System.Console;

Machine Create()
{
    Machine m = new Machine(239_740, "Bernette", "b33", (16, 14, 8), 255.55);

    return m;
}

void Present(Machine machine)
{
    WriteLine("Sewing Others");
    WriteLine("===========================================================");
    WriteLine("Machine Characteristics");
    WriteLine("-----------------------------------------------------------");
    WriteLine("Item Code:                {0}", machine.ItemCode);
    WriteLine("Make:                     {0}", machine.Make);
    WriteLine("Model:                    {0}", machine.Model);
    WriteLine("Dimensions (W x H x D):   {0} inches x {1} inches x {2} inches",
               machine.Dimensions.width, machine.Dimensions.height, machine.Dimensions.depth);
    WriteLine("Price:                   ${0}", machine.UnitPrice);
}

Machine product = Create();

Present(product);

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

internal ref struct Machine
{
    internal int ItemCode
    {
        get;
        init;
    }

    internal string? Make
    {
        init;
        get;
    }

    internal string? Model
    {
        get;
        init;
    }

    public double UnitPrice
    {
        init;
        get;
    }

    internal (int width, int height, int depth) Dimensions
    {
        get;
        init;
    }

    internal Machine(int code,
                     string? make,
                     string? model,
                    (int width, int height, int depth) dim,
                     double price)
    {
        (ItemCode, Make, Model, Dimensions, UnitPrice) = (code, make, model, dim, price);
    }
}

Practical LearningPractical Learning: Using a Structure Object

  1. Click the ReadSystemDatabase.cs tab and change the document as follows:
    using static System.Console;
    using RoadSystemDatabase;
    
    // Returning a structural object from a function
    Road Create()
    {
        Road rd = new Road();
    
        rd.Distance = 232.406;
        rd.Designation = "US 36";
        rd.RoadType = Category.USHighway;
        rd.End = "US-36 on CO-KS border";
        rd.Start = "Deer Ridge - US 34";
    
        return rd;
    }
    
    // Passing a structural object as argument
    void Show(object obj)
    {
        if (obj is null)
            return;
    
        Road rd = (Road)obj;
    
        WriteLine("Road System Database");
        WriteLine("============================================");
        WriteLine("Road Name: {0}", rd.Designation);
        WriteLine("Road Type: {0}", rd.RoadType);
        WriteLine("--------------------------------------------");
        WriteLine("Start:     {0}", rd.Start);
        WriteLine("End:       {0}", rd.End);
        WriteLine("Length:    {0:N} miles ({1:N} kilometers)", rd.Distance, rd.GetDistanceInKilometers());
        WriteLine("============================================");
    }
    
    Road rd = Create();
    Show(rd);
  2. To execute, on the main menu, click Debug -> Start Without Debugging:
    Road System Database
    ============================================
    Road Name: US 36
    Road Type: USHighway
    --------------------------------------------
    Start:     Deer Ridge - US 34
    End:       US-36 on CO-KS border
    Length:    232.41 miles (374.01 kilometers)
    ============================================
    
    Press any key to close this window . . .
  3. Press Z to close the window and return to your programming environment

Structures with Structures and Classes

A Field of a Structure Type

Once a structure exists, you can use it as a type. In a structure or a class, you can create a field that is of a structure type. There is nothing significant to do when declaring the variable. Probably the most important detail to keep in mind is that you must initialize the variable before using it. Here are examples:

using static System.Console;

Triangle tri = new Triangle();

WriteLine("Geometry");
WriteLine("=================================");
WriteLine("Triangle Characteristics");
WriteLine("---------------------------------");
tri.Present();
WriteLine("---------------------------------");
WriteLine("Distance 1:  {0}", tri.Distance1);
WriteLine("Distance 2:  {0}", tri.Distance2);
WriteLine("Distance 3:  {0}", tri.Distance3);
WriteLine("=================================");

internal struct Point
{
    internal int X { get; set; }
    internal int Y { get; set; }
}

internal class Triangle
{
    private Point point1;
    private Point point2;
    private Point point3;

    internal Triangle()
    {
        point1 = new Point();
        point1.X = 2;
        point1.Y = 3;

        point2 = new Point();
        point2.X = 4;
        point2.Y = 2;

        point3 = new Point();
        point3.X = 3;
        point3.Y = 6;
    }

    public double Distance1
    {
        get
        {
            return Math.Sqrt( ((point2.X - point1.X) * (point2.X - point1.X)) +
                              ((point2.Y - point1.Y) * (point2.Y - point1.Y)) );
        }
    }

    public double Distance2
    {
        get
        {
            return Math.Sqrt( ((point3.X - point2.X) * (point3.X - point2.X)) +
                              ((point3.Y - point2.Y) * (point3.Y - point2.Y)) );
        }
    }

    public double Distance3
    {
        get
        {
            return Math.Sqrt( ((point1.X - point3.X) * (point1.X - point3.X)) +
                              ((point1.Y - point3.Y) * (point1.Y - point3.Y)) );
        }
    }

    public void Present()
    {
        WriteLine("Point A({0}, {1})", point1.X, point1.Y);
        WriteLine("Point B({0}, {1})", point2.X, point2.Y);
        WriteLine("Point C({0}, {1})", point3.X, point3.Y);
    }
}

This would produce:

Geometry
=================================
Triangle Characteristics
---------------------------------
Point A(2, 3)
Point B(4, 2)
Point C(3, 6)
---------------------------------
Distance 1:  2.23606797749979
Distance 2:  4.123105625617661
Distance 3:  3.1622776601683795
=================================

Press any key to close this window . . .

A Property of a Structure Type

In a structure or a class, you can create a property that is a structure type. The rules are the same we reviewed for creating a property of a class. Here are examples:

using static System.Console;

Triangle tri = new Triangle();

tri.A = new Point(0, 2);
tri.B = new Point(7, 1);
tri.C = new Point(1, -1);

WriteLine("Geometry");
WriteLine("=================================");
WriteLine("Triangle Characteristics");
WriteLine("---------------------------------");
tri.Present();
WriteLine("---------------------------------");
WriteLine("Distance 1:  {0}", tri.Distance1);
WriteLine("Distance 2:  {0}", tri.Distance2);
WriteLine("Distance 3:  {0}", tri.Distance3);
WriteLine("=================================");

internal struct Point
{
    internal int X { get; set; }
    internal int Y { get; set; }

    internal Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

internal class Triangle
{
    private Point a;
    private Point b;
    private Point c;

    // A property of a structure type
    public Point A
    {
        get
        {
            return a;
        }

        set
        {
            a = value;
        }
    }

    // A property of a structure type
    public Point B
    {
        get
        {
            return b;
        }

        set
        {
            b = value;
        }
    }

    // A property of a structure type
    public Point C
    {
        get
        {
            return c;
        }

        set
        {
            c = value;
        }
    }

    public double Distance1
    {
        get
        {
            return Math.Sqrt( ((B.X - A.X) * (B.X - A.X)) +
                              ((B.Y - A.Y) * (B.Y - A.Y)) );
        }
    }

    public double Distance2
    {
        get
        {
            return Math.Sqrt( ((C.X - B.X) * (C.X - B.X)) +
                              ((C.Y - B.Y) * (C.Y - B.Y)) );
        }
    }

    public double Distance3
    {
        get
        {
            return Math.Sqrt( ((A.X - C.X) * (A.X - C.X)) +
                              ((A.Y - C.Y) * (A.Y - C.Y)) );
        }
    }

    public void Present()
    {
        WriteLine("Point A({0}, {1})", A.X, A.Y);
        WriteLine("Point B({0}, {1})", B.X, B.Y);
        WriteLine("Point C({0}, {1})", C.X, C.Y);
    }
}

    public Road Road1 { get; set; }
    public Road Road2 { get; set; }
    public string InOrNear { get; set; }

    public Intersection()
    {
    }

    public Intersection(Road one, Road two, string position)
    {
        Road1 = one;
        Road2 = two;
        InOrNear = position;
    }
}

After creating the property, you can use it as you see fit.

Structures and Inheritance

A structure is sealed from inheritance. This means that, when it comes to inheritance, a structure is subject to the following characteristics:

Structures and References

Passing a Structural Object by Reference

When you create a parameter in a method or a function and that parameter is a structure type, the technique is referred to as passing by value. A copy of the value of the structure is passed to the method or function. If the function or method modifies the argument, the original value would stay intact. If you want the function or method to modify the value of the structure, you can pass the argument by reference. You can do this using the (rules of the) ref keyword. Here is an example:

using static System.Console;

void Create(ref Machine sew)
{
    sew = new Machine(239_740, "Bernette", "b33", (16, 14, 8), 255.55);
}

void Present(Machine machine)
{
    WriteLine("Sewing Others");
    WriteLine("===========================================================");
    WriteLine("Machine Characteristics");
    WriteLine("-----------------------------------------------------------");
    WriteLine("Item Code:                {0}", machine.ItemCode);
    WriteLine("Make:                     {0}", machine.Make);
    WriteLine("Model:                    {0}", machine.Model);
    WriteLine("Dimensions (W x H x D):   {0} inches x {1} inches x {2} inches",
               machine.Dimensions.width, machine.Dimensions.height, machine.Dimensions.depth);
    WriteLine("Price:                   ${0}", machine.UnitPrice);
}

Machine product = new();

Create(ref product);

Present(product);

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

internal ref struct Machine
{
    internal int ItemCode { get; init; }
    internal string? Make { init; get; }
    internal string? Model { get; init; }
    public double UnitPrice { init; get; }
    internal (int width, int height, int depth) Dimensions { get; init; }

    internal Machine(int code,
                     string? make,
                     string? model,
                    (int width, int height, int depth) dim,
                     double price)
    {
        (ItemCode, Make, Model, Dimensions, UnitPrice) = (code, make, model, dim, price);
    }
}

You can also pass the referenced argument using the out keyword. Here is an example:

using static System.Console;

void Create(out Machine sew)
{
    sew = new Machine(239_740, "Bernette", "b33", (16, 14, 8), 255.55);
}

void Present(Machine machine)
{
    WriteLine("Sewing Others");
    WriteLine("===========================================================");
    WriteLine("Machine Characteristics");
    WriteLine("-----------------------------------------------------------");
    WriteLine("Item Code:                {0}", machine.ItemCode);
    WriteLine("Make:                     {0}", machine.Make);
    WriteLine("Model:                    {0}", machine.Model);
    WriteLine("Dimensions (W x H x D):   {0} inches x {1} inches x {2} inches",
               machine.Dimensions.width, machine.Dimensions.height, machine.Dimensions.depth);
    WriteLine("Price:                   ${0}", machine.UnitPrice);
}

Machine product;

Create(out product);

Present(product);

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

internal ref struct Machine
{
    internal int ItemCode { get; init; }
    internal string? Make { init; get; }
    internal string? Model { get; init; }
    public double UnitPrice { init; get; }
    internal (int width, int height, int depth) Dimensions { get; init; }

    internal Machine(int code,
                     string? make,
                     string? model,
                    (int width, int height, int depth) dim,
                     double price)
    {
        (ItemCode, Make, Model, Dimensions, UnitPrice) = (code, make, model, dim, price);
    }
}

A Referenced Structure

When creating a structure, you can indicate to the compiler that you want the objects created from that structure to be treated as reference type. To do this, before the struct keyword, type the ref keyword. Here is an example:

using static System.Console;

Machine Create()
{
    Machine m = new Machine((15.3, 12.0, 5.80), 162.88);
    
    m.ItemCode = 293_749;
    m.Model = "XR3774";
    m.Make = "Brother";

    return m;
}

void Present(Machine machine)
{
    WriteLine("Sewing Others");
    WriteLine("==============================================================");
    WriteLine("Machine Characteristics");
    WriteLine("--------------------------------------------------------------");
    WriteLine("Item Code:                {0}", machine.ItemCode);
    WriteLine("Make:                     {0}", machine.Make);
    WriteLine("Model:                    {0}", machine.Model);
    WriteLine("Dimensions (W x H x D):   {0} inches x {1} inches x {2} inches",
               machine.Dimensions.width, machine.Dimensions.height, machine.Dimensions.depth);
    WriteLine("Price:                   ${0}", machine.UnitPrice);
}

Machine product = Create();

Present(product);

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

internal ref struct Machine
{
    private string? _id;
    private int _code;
    private string? _company;

    public Machine((double width, double height, double depth) dim, double price)
    {
        Dimensions = dim;
        UnitPrice = price;
    }

    internal int ItemCode
    {
        get
        {
            return _code;
        }

        set
        {
            _code = value;
        }
    }

    internal string? Make
    {
        get
        {
            return _company;
        }

        set
        {
            _company = value;
        }
    }

    internal string? Model
    {
        get
        {
            return _id;
        }

        set
        {
            _id = value;
        }
    }

    internal (double width, double height, double depth) Dimensions
    {
        get;
        init;
    }

    public double UnitPrice
    {
        init;
        get;
    }
}

This would produce:

Sewing Others
==============================================================
Machine Characteristics
--------------------------------------------------------------
Item Code:                293749
Make:                     Brother
Model:                    XR3774
Dimensions (W x H x D):   15.3 inches x 12 inches x 5.8 inches
Price:                   $162.88
==============================================================

Press any key to close this window . . .

Notice that it is very simple to transform a regular structure into a referenced one. But once you apply that keyword, the structure becomes subject to new rules:

A Read-Only Referenced Structure

When creating a referenced structure, if you want the properties of the structure to be read-only, before the ref struct expression, type the readonly keyword. Here is an example:

// . . .

internal readonly ref struct Machine
{
    // . . .
}

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2001-2024, FunctionX Monday 26 December 2023 Next