Home

Classes and Indexers

 

Fundamentals of Indexed Properties and Classes

 

Introduction

In the previous lesson, we learned to create and use indexer that were taking parameters of primitive types. Just as we did with primitive types, you can create an indexer that is of a class type. For example, you can create a class so that one of the its fields declared as an array can be accessed with an index directly applied to an instance of the class.

Practical Learning: Introducing Indexers and Classes

  1. Start a new Console Application named PropertyRental2
  2. To create a new class, on the main menu, click Project -> Add Class...
  3. Set the Name to Property and press Enter
  4. Change the file as follows:
     
    using System;
    
    namespace PropertyRental2
    {
        public enum Condition
        {
            Excellent,
            Good,
            NeedsRepair,
            Unknown
        }
    
        public class Property
        {
            private long propCode;
            private Condition cond;
            private short beds;
            private float baths;
            private decimal val;
    
            public long PropertyCode
            {
                get { return propCode; }
                set { propCode = value; }
            }
    
            public Condition PropertyCondition
            {
                get { return cond; }
                set { cond = value; }
            }
    
            public short Bedrooms
            {
                get { return beds; }
                set { beds = value; }
            }
    
            public float Bathrooms
            {
                get { return (baths <= 0) ? 0.00f : baths; }
                set { baths = value; }
            }
    
            public decimal MonthlyRent
            {
                get { return (val <= 0) ? 0.00M : val; }
                set { val = value; }
            }
    
            public Property()
            {
                Random rnd = new Random();
                propCode = rnd.Next(100000, 999999);
                cond = Condition.Unknown;
                beds = 0;
                baths = 0.0f;
                val = 0.00M;
            }
    
            public override string ToString()
            {
                return "Property #:   " + PropertyCode +
                       "\nCondition:    " + PropertyCondition +
                       "\nBedrooms:     " + Bedrooms +
                       "\nBathrooms:    " + Bathrooms +
                       "\nMonthly Rent: " + MonthlyRent.ToString("C");
            }
        }
    }
  5. To create a new class, on the main menu, click Project -> Add Class...
  6. Set the Name to PropertyListing and press Enter
  7. Change the file as follows:
     
    using System;
    
    namespace PropertyRental2
    {
        public class PropertyListing
        {
            public Property[] props;
    
            public string this[long code]
            {
                get
                {
                    for (int i = 0; i < props.Length; i++)
                        if (code == props[i].PropertyCode)
                            return "Property #:   " + props[i].PropertyCode +
                                   "\nCondition:    " + 
    				props[i].PropertyCondition +
                                   "\nBedrooms:     " + props[i].Bedrooms +
                                   "\nBathrooms:    " + props[i].Bathrooms +
                                   "\nMonthly Rent: " + 
    				props[i].MonthlyRent.ToString("C");
                    return "Unidentifiable Property";
                }
            }
    
            public PropertyListing()
            {
                Random rnd = new Random();
                props = new Property[40];
    
                // Create a few properties ready to be rented
                props[0] = new Property();
                props[0].PropertyCode = rnd.Next(100000, 999999);
                props[0].PropertyCondition = Condition.Excellent;
                props[0].Bedrooms = 5;
                props[0].Bathrooms = 3.5f;
                props[0].MonthlyRent = 2650;
    
                props[1] = new Property();
                props[1].PropertyCode = rnd.Next(100000, 999999);
                props[1].PropertyCondition = Condition.Excellent;
                props[1].Bedrooms = 3;
                props[1].Bathrooms = 2.5f;
                props[1].MonthlyRent = 1750;
    
                props[2] = new Property();
                props[2].PropertyCode = rnd.Next(100000, 999999);
                props[2].PropertyCondition = Condition.Good;
                props[2].Bedrooms = 4;
                props[2].Bathrooms = 2.5f;
                props[2].MonthlyRent = 2450;
    
                props[3] = new Property();
                props[3].PropertyCode = rnd.Next(100000, 999999);
                props[3].PropertyCondition = Condition.Excellent;
                props[3].Bedrooms = 1;
                props[3].Bathrooms = 1.0f;
                props[3].MonthlyRent = 880;
    
                props[4] = new Property();
                props[4].PropertyCode = rnd.Next(100000, 999999);
                props[4].PropertyCondition = Condition.Excellent;
                props[4].Bedrooms = 3;
                props[4].Bathrooms = 2.5f;
                props[4].MonthlyRent = 1880;
    
                props[5] = new Property();
                props[5].PropertyCode = rnd.Next(100000, 999999);
                props[5].PropertyCondition = Condition.Good;
                props[5].Bedrooms = 2;
                props[5].Bathrooms = 1.0f;
                props[5].MonthlyRent = 1050;
    
                // Since we don't yet have a complete list of properties
                // Create some empty ones
                for (int i = 6; i < 40; i++)
                {
                    props[i] = new Property();
                }
            }
        }
    }
  8. Access the Program.cs file and change it as follows:
     
    using System;
    
    namespace PropertyRental2
    {
        public class Program
        {
            static int Main()
            {
                PropertyListing properties = new PropertyListing();
                long lngCode;
    
                Console.WriteLine("Here is a list of our properties by code");
                for (int i = 0; i < 6; i++)
                    Console.WriteLine("Property Code: {0}",
    			properties.props[i].PropertyCode);
    
                try
                {
                    Console.Write("Enter Property Code: ");
                    lngCode = long.Parse(Console.ReadLine());
    
                    Console.WriteLine("======================================");
                    Console.WriteLine("Property Information");
                    Console.WriteLine("--------------------------------------");
                    Console.WriteLine(properties[lngCode]);
                    Console.WriteLine("======================================");
    
                }
                catch (FormatException)
                {
                    Console.WriteLine("=- Invalid Property Code -=");
                }
    
                return 0;
            }
        }
    }
  9. Press Ctrl + F5 to execute the application. Here is an example:
     
    Here is a list of our properties by code
    Property Code: 355443
    Property Code: 653004
    Property Code: 800118
    Property Code: 839375
    Property Code: 148561
    Property Code: 697001
    Enter Property Code: 697001
    ======================================
    Property Information
    --------------------------------------
    Property #:   697001
    Condition:    Good
    Bedrooms:     2
    Bathrooms:    1
    Monthly Rent: $1,050.00
    ======================================
    Press any key to continue . . .
  10. Close the DOS window

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. Here is an example of a simple class:

public class Student
{
    public string FirstName;
    public string LastName;
    public int Gender;
}

When creating the class that will host the indexed property, declare an array field for the class. Then, create the this property with the desired accessor(s). Here is an example:

public class Student
{
    public string FirstName;
    public string LastName;
    public int Gender;
}

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

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

After creating the indexing class, you can use it and access the indexer; for example, you can retrieve its value(s). Here is an example:

using System;

public class Student
{
    public string FirstName;
    public string LastName;
    public int Gender;
}

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

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

    public SchoolRegistration()
    {
        std[0] = new Student();
        std[0].FirstName = "Alfredo";
        std[0].LastName = "Olmos";
        std[0].Gender = 2;

        std[1] = new Student();
        std[1].FirstName = "Patricia";
        std[1].LastName = "Katts";
        std[1].Gender = 1;

        std[2] = new Student();
        std[2].FirstName = "Josiane";
        std[2].LastName = "Euler";
        std[2].Gender = 1;

        std[3] = new Student();
        std[3].FirstName = "Joan";
        std[3].LastName = "Jones";
        std[3].Gender = 3;

        std[4] = new Student();
        std[4].FirstName = "George";
        std[4].LastName = "Paulson";
        std[4].Gender = 2;
    }
}

public class Program
{
    static int Main()
    {
        SchoolRegistration pupils = new SchoolRegistration();

        for (int i = 0; i < 5; i++)
        {
	    Student pupil = pupils[i];

            Console.WriteLine("Student Information");
            Console.WriteLine("---------------------");
            Console.WriteLine("First Name: {0}", pupil.FirstName);
            Console.WriteLine("Last Name:  {0}", pupil.LastName);
            Console.WriteLine("Gender:     {0}\n",
                             (pupil.Gender == 1 ? "Female" :
                             (pupil.Gender == 2 ? "Male" : "Unknown")));
        }

        return 0;
    }
}

This would produce:

Student Information
---------------------
First Name: Alfredo
Last Name:  Olmos
Gender:     Male

Student Information
---------------------
First Name: Patricia
Last Name:  Katts
Gender:     Female

Student Information
---------------------
First Name: Josiane
Last Name:  Euler
Gender:     Female

Student Information
---------------------
First Name: Joan
Last Name:  Jones
Gender:     Unknown

Student Information
---------------------
First Name: George
Last Name:  Paulson
Gender:     Male

Press any key to continue . . .

Practical Learning: Using an Integer-Based Indexer

  1. To create an indexer that takes an integer and returns an object, access the PropertyListing.cs file and change it as follows:
     
    using System;
    
    namespace PropertyRental1
    {
        public class PropertyListing
        {
            public Property[] props;
    
            public Property this[int i]
            {
                get { return props[i]; }
            }
    
            public PropertyListing()
            {
                . . . No Change
            }
        }
    }
  2. Access the Program.cs file and change it as follows:
     
    using System;
    
    namespace PropertyRental2
    {
        public class Program
        {
            static int Main()
            {
                PropertyListing properties = new PropertyListing();
    
                Console.WriteLine("Here is a list of our properties");
                for (int i = 0; i < 6; i++)
                {
                    Property prop = properties[i];
    
                    Console.WriteLine("Property Information");
                    Console.WriteLine("--------------------------------------");
                    Console.WriteLine("Property #:   {0}", prop.PropertyCode);
                    Console.WriteLine("Condition:    {0}", 
    			prop.PropertyCondition);
                    Console.WriteLine("Bedrooms:     {0}", prop.Bedrooms);
                    Console.WriteLine("Bathrooms:    {0}", prop.Bathrooms);
                    Console.WriteLine("Monthly Rent: {0}",
    			prop.MonthlyRent.ToString("C"));
                    Console.WriteLine("======================================");
                }
    
                return 0;
            }
        }
    }
  3. Press Ctrl + F5 to execute the application
  4. Close the DOS window

An Indexed Property Using Another Primitive Type

The above implementation of the SchoolRegistration class easily allowed us to locate an element of the array by specifying its integer-based index. As done for primitive types, an indexer can take a parameter other than an integer. In some cases, you may use your class or a class created by someone else and need to access an element of the array without information other than its index. Consider the following program:

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 +
                     "\nFirst Name: " + FirstName +
                     "\nLast Name:  " + LastName +
                     "\nGender:     " + Gender;
        return str;
    }
}

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

    public Student this[...]
    {
    }
}

Previously, we saw that you could create an indexer that takes a type than an integer. For example, we saw that a string could be used as an index.

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. In reality, 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. This is better if that field holds unique values among the other elements of the array. For our Student class, we could use the StudentID field (because we will make sure that each student has a unique ID). You can start the property as follows:

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

    public Student this[long id]
    {
    }
}

When a user uses this property, he or she must provide a value that uniquely identifies an element of the array. You in turn, when you get this 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 how 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;
        }
    }
}

After creating the indexer, you can use it. Once again, you must follow the rules of a method that takes an argument and returns a value other than void. In this case, the indexer must take a string and it must return a Student object. Here is an example:

using System;

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 +
                     "\nFirst Name: " + FirstName +
                     "\nLast Name:  " + LastName +
                     "\nGender:     " + Gender;
        return str;
    }
}

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

    public SchoolRegistration()
    {
        students[0] = new Student();
        students[0].StudentID = 917294;
        students[0].FirstName = "Helene";
        students[0].LastName = "Mukoko";
        students[0].Gender = Classification.Female;

        students[1] = new Student();
        students[1].StudentID = 283764;
        students[1].FirstName = "Patrice";
        students[1].LastName = "Katts";
        students[1].Gender = Classification.Unknown;

        students[2] = new Student();
        students[2].StudentID = 192046;
        students[2].FirstName = "Armand";
        students[2].LastName = "Essono";
        students[2].Gender = Classification.Male;

        students[3] = new Student();
        students[3].StudentID = 618268;
        students[3].FirstName = "Bertrand";
        students[3].LastName = "Yamaguchi";
        students[3].Gender = Classification.Male;

        students[4] = new Student();
        students[4].StudentID = 820648;
        students[4].FirstName = "Hortense";
        students[4].LastName = "McNeal";
        students[4].Gender = Classification.Female;
    }
}

public class Program
{
    static int Main()
    {
        SchoolRegistration pupils = new SchoolRegistration();

        Student pupil = pupils[820648];

        Console.WriteLine("Student Information");
        Console.WriteLine("---------------------");
        Console.WriteLine("First Name: {0}", pupil.FirstName);
        Console.WriteLine("Last Name:  {0}", pupil.LastName);
        Console.WriteLine("Gender:     {0}\n", pupil.Gender);

        pupil = pupils[192046];

        Console.WriteLine("Student Information");
        Console.WriteLine("---------------------");
        Console.WriteLine("First Name: {0}", pupil.FirstName);
        Console.WriteLine("Last Name:  {0}", pupil.LastName);
        Console.WriteLine("Gender:     {0}\n", pupil.Gender);

        return 0;
    }
}

This would produce:

Student Information
---------------------
First Name: Hortense
Last Name:  McNeal
Gender:     Female

Student Information
---------------------
First Name: Armand
Last Name:  Essono
Gender:     Male

Press any key to continue . . .
 

Home Copyright © 2007-2013, FunctionX Next