Fundamentals of Indexed Properties and Classes

Introduction

Just as done for primitive types, you can create an indexer that is of a class type. For example, you can create a class so that one of its fields declared as an array can be accessed with an index directly applied to an instance of the class.

An Integer-Based Indexed Property

Before designing an indexer that is class-based, first create the class that will be used as the data type. The class can be simple or complex as you judge it necessary.

An Indexed Property Using Another Primitive Type

As done for primitive types, an indexer can take a parameter other than a regular integer. For example, you can use a Boolean type or another type of integer. By now, we know that a basic indexed property produces (or all the indexed properties we have studied so far produce) only one value. If you have a class that has only one field, this would be enough. Most of the time, a class has many fields. In such a case, when you create an indexer , you need to be able to refer to one exact element of the array. To make this possible, you must define a way to point to the particular element you want. One way you can do this is to use one field of the class as a reference. You can start the property as follows:

public class SchoolRegistration
{
    Student[] std = new Student[5];

    public Student this[long id]
    {
    }
}

To use such a property, you must provide a value that uniquely identifies an element of the array. When you get such a value, you can search for it in the array. If you find it and the array has a get accessor, you can then return the desired but appropriate value. Here is a way this can be done:

public class SchoolRegistration
{
    Student[] students = new Student[50];

    public Student this[long id]
    {
        get
        {
            for (int i = 0; i < students.Length; i++)
            {
                if (students[i].StudentID == id)
                    return students[i];
            }
            // Unknown student or the number was not found
            return null;
        }
    }
}

Topics on Indexed Properties and Classes

A Class as Index

As opposed to returning a class, an indexer can use a class as its index. Here is a class to start with:

public class MetroStation
{
    public bool Parking { get; set; }
    public int BikeRacks { get; set; }
    public string LineName { get; set; }
    public string Location { get; set; }
    public string StationName { get; set; }
    public short StationNumber { get; set; }

    public MetroStation(short id)
    {
        BikeRacks = id;
    }

    public MetroStation(short id, string station, string address, string lName)
    {
        StationNumber = id;
        StationName   = station;
        Location      = address;
        LineName      = lName;
    }

    public MetroStation(short id, string station, string address, bool parking, int bike, string lName)
    {
        StationNumber = id;
        StationName   = station;
        Location      = address;
        Parking       = parking;
        BikeRacks     = bike;
        LineName      = lName;
    }

    public override string ToString()
    {
        return $"{StationName} (#{StationNumber}, in {Location})";
    }
}

To create an indexed property that uses an object as index, the primary action you must take is to include a class and its name as a parameter to the this property. You can start such a class as follows:

public class Line
{
    public string this[MetroStation station]
    {
    }
}

When implementing the class, you should proceed the same way we have done so far following the rules of a method that takes an argument and returns a value other than void. Here is an example:

public class Line
{
    public string Color   { get; set; }
    public string Between { get; set; }
    public string And     { get; set; }

    private MetroStation[] stations = new MetroStation[10];

    public string this[MetroStation station]
    {
        get
        {
            for(int i = 0; i < stations.Length - 1; i++)
            {
                if( station.StationNumber == stations[i].StationNumber)
                {
                    return stations[i].ToString();
                }
            }

            return "";
        }
    }

    public Line()
    {
        stations = new MetroStation[12];

        stations[0]  = new MetroStation(2014, "Shady Grove", "Rockville", true, 32, "Red");
        stations[1]  = new MetroStation(1862, "Greenbelt", "Greenbelt", true, 60, "Green");
        stations[2]  = new MetroStation(9794, "Fort Totten", "DC", true, 10, "Yellow");
        stations[3]  = new MetroStation(1660, "Rockville", "Rockville", true, 69, "Red");
        stations[4]  = new MetroStation(9722, "White Flint", "Rockville", true, 32, "Red");
        stations[5]  = new MetroStation(1862, "Greenbelt", "Greenbelt", true, 60, "Green");
        stations[6]  = new MetroStation(1862, "Greenbelt", "Greenbelt", true, 60, "Yellow");
        stations[7]  = new MetroStation(3940, "College Park-U of MD", "College Park", true, 81, "Green");
        stations[8]  = new MetroStation(1417, "Forest Glen", "Silver Spring", true, 42, "Red");
        stations[9]  = new MetroStation(9737, "Wheaton", "Silver Spring", true, 36, "Red");
        stations[10] = new MetroStation(8802, "Glenmont", "Silver Spring", true, 36, "Red");
        stations[11] = new MetroStation(9273, "U Street/ African - Amer Civil War Memorial", "DC", false, 0, "Yellow");
    }
}

After creating the property, you can use it. To do this, you must pass an object that is the type of the index. You can then use the returned value as you see fit. Here is an example:

Console.WriteLine("Metropolitan Transit System");

Line ln = new Line();
MetroStation ms = new MetroStation(9722, "White Flint", "Rockville", "Red");

ln.Between = "Rockville";
ln.And = "Silver Spring";
string station = ln[ms];

Console.WriteLine("-------------------------------------------------------------------");
Console.WriteLine("Station: {0} station on the {1} line", station, ms.LineName);
Console.WriteLine("         Transit from {0} to {1}", ln.Between, ln.And);
Console.WriteLine("===================================================================");

public class MetroStation
{
    public bool    Parking       { get; set; }
    public int     BikeRacks     { get; set; }
    public string? LineName      { get; set; }
    public string? Location      { get; set; }
    public string? StationName   { get; set; }
    public short   StationNumber { get; set; }

    public MetroStation(short id)
    {
        BikeRacks = id;
    }

    public MetroStation(short id, string station, string address, string lName)
    {
        StationNumber = id;
        StationName   = station;
        Location      = address;
        LineName      = lName;
    }

    public MetroStation(short id, string station, string address, bool parking, int bike, string lName)
    {
        StationNumber = id;
        StationName   = station;
        Location      = address;
        Parking       = parking;
        BikeRacks     = bike;
        LineName      = lName;
    }

    public override string ToString()
    {
        return $"{StationName} (#{StationNumber}, in {Location})";
    }
}

public class Line
{
    public string? Color   { get; set; }
    public string? Between { get; set; }
    public string? And     { get; set; }

    private MetroStation[] stations = new MetroStation[10];

    public string this[MetroStation station]
    {
        get
        {
            for (int i = 0; i < stations.Length - 1; i++)
            {
                if (station.StationNumber == stations[i].StationNumber)
                {
                    return stations[i].ToString();
                }
            }

            return string.Empty;
        }
    }

    public Line()
    {
        stations     = new MetroStation[12];

        stations[0]  = new MetroStation(2014, "Shady Grove",          "Rockville",     true, 32, "Red"   );
        stations[1]  = new MetroStation(1862, "Greenbelt",            "Greenbelt",     true, 60, "Green" );
        stations[2]  = new MetroStation(9794, "Fort Totten",          "DC",            true, 10, "Yellow");
        stations[3]  = new MetroStation(1660, "Rockville",            "Rockville",     true, 69, "Red"   );
        stations[4]  = new MetroStation(9722, "White Flint",          "Rockville",     true, 32, "Red"   );
        stations[5]  = new MetroStation(1862, "Greenbelt",            "Greenbelt",     true, 60, "Green" );
        stations[6]  = new MetroStation(1862, "Greenbelt",            "Greenbelt",     true, 60, "Yellow");
        stations[7]  = new MetroStation(3940, "College Park-U of MD", "College Park",  true, 81, "Green" );
        stations[8]  = new MetroStation(1417, "Forest Glen",          "Silver Spring", true, 42, "Red"   );
        stations[9]  = new MetroStation(9737, "Wheaton",              "Silver Spring", true, 36, "Red"   );
        stations[10] = new MetroStation(8802, "Glenmont",             "Silver Spring", true, 36, "Red"   );
        stations[11] = new MetroStation(9273, "U Street/ African - Amer Civil War Memorial", "DC", false, 0, "Yellow");
    }
}

This would produce:

Metropolitan Transit System
-------------------------------------------------------------------
Station: White Flint (#9722, in Rockville) station on the Red line
         Transit from Rockville to Silver Spring
===================================================================
Press any key to close this window . . .

You can also directly pass an instance of the class in the square brackets of the object that holds the indexed property, as long as you specify the object. Here is an example:

Console.Title = "Metropolitan Transit System";

Line ln = new();
MetroStation ms = new(9722, "White Flint", "Rockville", "Red");

ln.Between     = "Rockville";
ln.And         = "Silver Spring";
string station = ln[ms];

Console.WriteLine("Metropolitan Transit System");

Console.WriteLine("-------------------------------------------------------------------");
Console.WriteLine("Station: {0} station on the {1} line", station, ms.LineName);
Console.WriteLine("         Transit from {0} to {1}", ln.Between, ln.And);
Console.WriteLine("===================================================================");

ln = new Line();

ln.Between = "Greenbelt";
ln.And = "Franconia-Springfield";
station = ln[new MetroStation(1862, "Greenbelt", "Greenbelt", "Green")];

Console.WriteLine("Station: {0} station on the {1} line",
                  station, (new MetroStation(1862, "Greenbelt", "Greenbelt", "Green")).LineName);
Console.WriteLine("         Transit from {0} to {1}", ln.Between, ln.And);
Console.WriteLine("===================================================================");

public class MetroStation
{
    public bool    Parking       { get; set; }
    public int     BikeRacks     { get; set; }
    public string? LineName      { get; set; }
    public string? Location      { get; set; }
    public string? StationName   { get; set; }
    public short   StationNumber { get; set; }

    public MetroStation(short id) => BikeRacks = id;

    public MetroStation(short id, string station, string address, string lName)
    {
        (StationNumber, StationName, Location, LineName) = (id, station, address, lName);
    }

    public MetroStation(short id, string station, string address, bool parking, int bike, string lName)
    {
        (StationNumber, StationName,
         Location, Parking, BikeRacks, LineName) = (id, station, address, parking,  0, lName);
    }

    public override string ToString()
    {
        return $"{StationName} (#{StationNumber}, in {Location})";
    }
}

public class Line
{
    public string? Color   { get; set; }
    public string? Between { get; set; }
    public string? And     { get; set; }

    readonly MetroStation[] stations = new MetroStation[10];

    public string this[MetroStation station]
    {
        get
        {
            for (int i = 0; i < stations.Length - 1; i++)
            {
                if (station.StationNumber == stations[i].StationNumber)
                {
                    return stations[i].ToString();
                }
            }

            return string.Empty;
        }
    }

    public Line()
    {
        stations     = new MetroStation[12];

        stations[0]  = new MetroStation(2014, "Shady Grove",          "Rockville",     true, 32, "Red"   );
        stations[1]  = new MetroStation(1862, "Greenbelt",            "Greenbelt",     true, 60, "Green" );
        stations[2]  = new MetroStation(9794, "Fort Totten",          "DC",            true, 10, "Yellow");
        stations[3]  = new MetroStation(1660, "Rockville",            "Rockville",     true, 69, "Red"   );
        stations[4]  = new MetroStation(9722, "White Flint",          "Rockville",     true, 32, "Red"   );
        stations[5]  = new MetroStation(1862, "Greenbelt",            "Greenbelt",     true, 60, "Green" );
        stations[6]  = new MetroStation(1862, "Greenbelt",            "Greenbelt",     true, 60, "Yellow");
        stations[7]  = new MetroStation(3940, "College Park-U of MD", "College Park",  true, 81, "Green" );
        stations[8]  = new MetroStation(1417, "Forest Glen",          "Silver Spring", true, 42, "Red"   );
        stations[9]  = new MetroStation(9737, "Wheaton",              "Silver Spring", true, 36, "Red"   );
        stations[10] = new MetroStation(8802, "Glenmont",             "Silver Spring", true, 36, "Red"   );
        stations[11] = new MetroStation(9273, "U Street/ African - Amer Civil War Memorial", "DC", false, 0, "Yellow");
    }
}

This would produce:

Metropolitan Transit System
-------------------------------------------------------------------
Station: White Flint (#9722, in Rockville) station on the Red line
         Transit from Rockville to Silver Spring
-------------------------------------------------------------------
Station: Greenbelt (#1862, in Greenbelt) station on the Green line
         Transit from Greenbelt to Franconia-Springfield
===================================================================
Press any key to close this window . . .

Overloading a Class-Based Indexed Property

As mentioned for indexers that return primitive types, you can overload an indexed property that produces an object or an indexed property that uses an object as index. You do this following the same rules applied to method overloading and arrays:

Read/Write Indexed Properties of Class Types

As done for a primitive type, you can allow the clients of your indexer to assign values to the array's elements. Once again, when defining the property, you should include a set accessor to it. In the set accessor, you should assign the value keyword to an element of the array. Here is an example:

public class SchoolRegistration
{
    Student[] std = new Student[5];

    public Student this[int i]
    {
        get { return std[i]; }
        set { std[i] = value; }
    }
}

After doing this, you can create an element of the array by applying the square brackets to the instance of the class and assigning the desired value to it. The problem with the class is that, since it may have many fields (or properties), to completely define each element, you must provide a value to the members (fields and/or properties) of the class itself. Here is an example:

public enum Classification
{
    Female,
    Male,
    Unknown
}

public class Student
{
    public long StudentID;
    public string firstName;
    public string lastName;
    public Classification Gender;

    public override string ToString()
    {
        string str = "Student ID: " + StudentID + " " +
                     "First Name: " + firstName + " " +
                     "Last Name:  " + lastName + " " +
                     "Gender:     " + Gender;
        return str;
    }
}

public class SchoolRegistration
{
    Student[] std = new Student[5];

    public Student this[int i]
    {
        get { return std[i]; }
        set { std[i] = value; }
    }
}

public class Exercise
{
    private void Create()
    {
        var registration = new SchoolRegistration();

        var stud = new Student();
        stud.StudentID = 604057;
        stud.firstName = "Gertrude";
        stud.lastName = "Monayong";
        stud.Gender = Classification.Female;
        registration[2] = stud;
    }
}

Previous Copyright © 2008-2024, FunctionX Friday 22 October 2021 Next