Monday, November 1, 2021

C# Indexer With Three Possible Cases


Indexer is a special type of property in C# class or struct which allows you to use object of class/struct with index similar to an array. For example, if Employee is a class or struct then indexer will be employee[j] where employee is an object of the class or struct and j is index value (starting from 0) used to get or set the value of element of a collection type field of the class or struct based on their index positions.

But indexer can do other functions as well. For example, if field is a class or struct type data then properties of this class or struct can be accessed as per their index position.

Also, indexer can be used to refer a number of fields of its class or struct. If indexer class contains a number of fields/properties of same return type then they can be accessed using indexer as per their defined index position.

We will see all these different three cases one by one.

Apart from these functions of an indexer, we will see how an indexer can be overloaded like a method.

In a class or struct, usually there is a specific property corresponding to each field which helps to get or set that field; property is just a medium via which we get or set its field. Indexer is also a property but a specialized one. Unlike a property, it has no name. A property cannot be overloaded but indexer can be.

Indexer is C# language construct with the help of which, an object of class is converted into a virtual array and by giving index values to the object of the class, we can get or set any field of that class or other functions as mentioned before.

access_modifier return_type this( parameter_type parameter_name)
{
    get{ // conditional logic if any and return statement}
    set{ // conditional logic before setting value and then set value}
}

In the above example, the index variable is known as parameter of indexer. The access modifier of the indexer can be public, protected, etc. similar to a property. Its body contains get and set blocks similar to property.

Suppose you want to access a number of fields of indexer class using indexer then these fields must have same return type. This type must match the return type of the indexer. For example, suppose some of the fields of a class are of type string, then in such a case we will give the type string of the indexer to access these fields. But what if the fields of the class are of different types and you want to access them, then in such a case the return type of the indexer must be object.

We write the logic to get a field within the get block whereas write the logic to set a field within the set block inside the indexer.

We have seen that indexer can be used to refer a number of fields of its class or struct. If indexer class contains a number of fields/properties of same return type then they can be accessed using indexer as per their defined index position. Now let us understand this case about indexer with an example.

namespace IndexerExample
{
    class Employee
    {
        private int EmpId;
        private string EmpName;
        private string Department;
        private double Salary;

        public Employee(int EmpId, string EmpName, string Department, double Salary)
        {
            this.EmpId = EmpId;
            this.EmpName = EmpName;
            this.Department = Department;
            this.Salary = Salary;
        }

        public object this[int index]
        {
            get
            {
                if (index == 0)
                    return EmpId;
                else if (index == 1)
                    return EmpName;
                else if (index == 2)
                    return Department;
                else if (index == 3)
                    return Salary;
                else
                    return "";
            }
            set
            {
                //indexer returns object by default
                //We typecast to appropriate type as per the type of field
                if (index == 0)
                    EmpId = (int)value;
                else if (index == 1)
                    EmpName = (string)value;
                else if (index == 2)
                    Department = (string)value;
                else if (index == 3)
                    Salary = (double)value;
                else
                    throw new Exception("Invalid index");
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Employee employee = new Employee(1, "Ajeet", "Sales", 20000);
            Console.WriteLine("GET the old values:");
            Console.WriteLine("ID: " + employee[0]);
            Console.WriteLine("Name: " + employee[1]);
            Console.WriteLine("Department: " + employee[2]);
            Console.WriteLine("Salary: " + employee[3]);
            Console.WriteLine("\nSET the new values:");
            employee[0] = 2;
            employee[1] = "Rajan";
            employee[2] = "IT";
            employee[3] = 23560.50;
            Console.WriteLine("ID: " + employee[0]);
            Console.WriteLine("Name: " + employee[1]);
            Console.WriteLine("Department: " + employee[2]);
            Console.WriteLine("Salary: " + employee[3]);
            Console.ReadLine();
        }
    }
}
The case of using class or struct type data as a field of class and then using indexer to access the properties of this class as field is demonstarted in the following code. We create Employee class which is used inside IndexerDemo class as a field. The IndexerDemo class uses indexer to access the properties of Employee type field.
namespace IndexerExample
{
    class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string Department { get; set; } = string.Empty;
        public double Salary { get; set; }
        public Employee() { }
        public Employee(int EmpId, string EmpName, string Department, double Salary)
        {
            this.Id = EmpId;
            this.Name = EmpName;
            this.Department = Department;
            this.Salary = Salary;
        }

    }
    class IndexerDemo
    {
        public Employee Emp { get; set; } = new Employee();
        public IndexerDemo(Employee employee)
        {
            if (employee != null)
            {
                Emp.Id = employee.Id;
                Emp.Name = employee.Name;
                Emp.Department = employee.Department;
                Emp.Salary = employee.Salary;
            }
            else
            {
                throw new ArgumentNullException(nameof(employee), "Employee cannot be null");
            }
        }
        public object this[int index]
        {
            get
            {
                if (index == 0)
                    return Emp.Id;
                else if (index == 1)
                    return Emp.Name;
                else if (index == 2)
                    return Emp.Department;
                else if (index == 3)
                    return Emp.Salary;
                else
                    throw new Exception("Invalid index");
            }
            set
            {
                //indexer returns object by default
                //We typecast to appropriate type as per the type of field
                if (index == 0)
                    Emp.Id = (int)value;
                else if (index == 1)
                    Emp.Name = (string)value;
                else if (index == 2)
                    Emp.Department = (string)value;
                else if (index == 3)
                    Emp.Salary = (double)value;
                else
                    throw new Exception("Invalid index");
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Employee employee = new Employee(1, "Ajeet", "Sales", 20000);
            IndexerDemo demo = new IndexerDemo(employee);
            Console.WriteLine("GET the old values:");
            Console.WriteLine("ID: " + demo[0]);
            Console.WriteLine("Name: " + demo[1]);
            Console.WriteLine("Department: " + demo[2]);
            Console.WriteLine("Salary: " + demo[3]);
            Console.WriteLine("\nSET the new values:");
            demo[0] = 2;
            demo[1] = "Rajan";
            demo[2] = "IT";
            demo[3] = 23560.50;
            Console.WriteLine("ID: " + demo[0]);
            Console.WriteLine("Name: " + demo[1]);
            Console.WriteLine("Department: " + demo[2]);
            Console.WriteLine("Salary: " + demo[3]);
            Console.ReadLine();
        }
    }
}
The cases of field as collection type for indexer and multiple parameters indexer are explained in the following post:
C# Indexer Explained for Beginners

No comments:

Post a Comment

Hot Topics