Sunday 12 February 2012

Introduction to Indexers with Asp.net(C#)

A Property Can Be Indexed

 
Introduction


We know how to create an array, how to assign values to its elements, and how to get the value 
of each element. 
Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        double[] Numbers = new double[5];

        Numbers[0] = 927.93;
        Numbers[1] = 45.155;
        Numbers[2] = 2.37094;
        Numbers[3] = 73475.25;
        Numbers[4] = 186.72;

        for (int i = 0; i < Numbers.Length; i++)
            Response.Write("<pre>Number " + (i + 1) + ": " + Numbers[i] + 
"<br />");
        Response.Write("</pre>");
    }
}

This would produce:
Introduction to Indexers

In the same way, if we declared an array as a member variable of a class, to access the elements of that member, we had to use an instance of the class, followed by the period operator, followed by the member variable applied with the square brackets. Instead of accessing each element through its member variable, you can create a type of property referred to as an indexer.

An Indexer


An indexer, also called an indexed property, is a class's property that allows you to access a member variable of a class using the features of an array. To create an indexed property, start the class like any other. In the body of the class, create a field that is an array. Here is an example:
public class Number
{
    double[] numbers = new double[5];
}
Then, in the body of the class, create a property named this with its accessor(s). The this property must be the same type as the field it will refer to. The property must take a parameter as an array. This means that it must have square brackets. Inside of the brackets, include the parameter you will use as index to access the members of the array.
Traditionally, you usually access the members of an array using an integer-based index. Therefore, you can use an int type as the index of the array. Of course, the index's parameter must have a name, such as i. This would be done as follows:
public class Number
{
    double[] numbers = new double[5];

    public double this[int i]
    {
    }
}

If you want the property to be read-only, include only a get accessor. In the get accessor, you should return an element of the array field the property refers to, using the parameter of the property. This would be done as follows:
public class Number
{
    double[] numbers = new double[5];

    public double this[int i]
    {
        get { return numbers[i]; }
    }
}

Once you have created the indexed property, the class can be used. To start, you can declare a variable of the class. To access its arrayed field, you can apply the square brackets directly to it. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Number
{
    double[] numbers;

    public double this[int i]
    {
        get { return numbers[i]; }
    }

    public Number()
    {
        numbers = new double[5];
        numbers[0] = 927.93;
        numbers[1] = 45.155;
        numbers[2] = 2.37094;
        numbers[3] = 73475.25;
        numbers[4] = 186.72;
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var nbr = new Number();

        Response.Write("<pre>");
        for (int i = 0; i < 5; i++)
            Response.Write("Number " + (i + 1) + ": " + nbr[i] + "<br />");
        Response.Write("</pre>");
    }
}

Based on this, a type of formula to create and use a basic indexed property is:
class ClassName
{
    DataType[] ArrayName = new DataType[Length];

    public DataType this[int i]
    {
        get { return ArrayName[i]; }
    }
}

Indexed Properties of Other Primitive Types


As opposed to an int or a double, you can also create a property that takes or produces a string. To do this, you can use the above class template with the desired data type, such as string. Here is an example of a string-based indexed property:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Philosopher
{
    string[] phil = new string[8];

    public string this[int i]
    {
        get { return phil[i]; }
    }

    public Philosopher()
    {
        phil[0] = "Aristotle";
        phil[1] = "Emmanuel Kant";
        phil[2] = "Tom Huffman";
        phil[3] = "Judith Jarvis Thompson";
        phil[4] = "Thomas Hobbes";
        phil[5] = "Cornell West";
        phil[6] = "Jane English";
        phil[7] = "James Rachels";
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var thinker = new Philosopher();

        Response.Write("<pre>");
        for (int i = 0; i < 8; i++)
            Response.Write("Philosopher: " + thinker[i] + "<br />");
        Response.Write("</pre>");
    }
}
This would produce:
Indexers
In the same way, you can create a Boolean-based indexed property by simply making it return a bool type.

Using a Non-Integer-Based Index


We know how to create different arrays that are numeric or string based. Here is an example of a float array:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        float[] ages = new float[5];

        ages[0] = 14.50f;
        ages[1] = 12.00f;
        ages[2] = 16.50f;
        ages[3] = 14.00f;
        ages[4] = 15.50f;

        Response.Write("<pre>Student Age: " + ages[2] + "</pre>");
    }
}
When we think of arrays, we usually consider passing an integer-based parameter to the square brackets of the variable, as done for the above ages array:
float[] ages = new float[5];
When using an indexed property, you can use almost any type of index, such as a real value or a string. To do this, in the square brackets of the this property, pass the desired type as the index. Here is an example:
public class StudentAge
{
    public float this[string name]
    {
    }
}
When defining the indexed property, there are two rules you must follow and you are aware of them already because an indexed property is like a method that takes a parameter and doesn't return void. Therefore, when implementing an indexed property, make sure you return the right type of value and make sure you pass the appropriate index to the return value of the this property. Here is an example:
public class StudentAge
{
    public float this[string name]
    {
        get
        {
            if(  name == "Ernestine Jonas" )
                return 14.50f;
            else if( name == "Paul Bertrand Yamaguchi" )
                return 12.50f;
            else if( name == "Helene Jonas" )
                return 16.00f;
            else if( name == "Chrissie Hanson" )
                return 14.00f;
            else if( name == "Bernard Hallo" )
                return 15.50f;
            else
                return 12.00f;
        }
    }
}

Once you have defined the property, you can use it. To access any of its elements, you must pass the appropriate type of index. In this case, the index must be passed as a string and not an integer. You can then do whatever you want with the value produced by the property. For example, you can show it to the web page visitor. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class StudentAge
{
    public float this[string name]
    {
        get
        {
            if (name == "Ernestine Jonas")
                return 14.50f;
            else if (name == "Paul Bertrand Yamaguchi")
                return 12.50f;
            else if (name == "Helene Jonas")
                return 16.00f;
            else if (name == "Chrissie Hanson")
                return 14.00f;
            else if (name == "Bernard Hallo")
                return 15.50f;
            else
                return 12.00f;
        }
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var sa = new StudentAge();
        var age = sa["Paul Bertrand Yamaguchi"];

        Response.Write("Student Age: " + age);
    }
}
This would produce:
Indexer
You can also pass an enumeration as an index. To do this, after defining the enumeration, type its name and a parameter name in the square brackets of the this member, then define the property as you see fit. To access the property outside, apply an enumeration member to the square brackets on an instance of the class. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public enum CategoryFee
{
    Children,
    Adult,
    Senior,
    Unknown
}

public class GolfClubMembership
{
    double[] fee = new double[4];

    public GolfClubMembership()
    {
        fee[0] = 150.95d;
        fee[1] = 250.75d;
        fee[2] = 85.65d;
        fee[3] = 350.00d;
    }

    public double this[CategoryFee cat]
    {
        get
        {
            if (cat == CategoryFee.Children)
                return fee[0];
            else if (cat == CategoryFee.Adult)
                return fee[1];
            else if (cat == CategoryFee.Senior)
                return fee[2];
            else
                return fee[3];
        }
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var mbr = new GolfClubMembership();

        Response.Write("Membership Fee: " + mbr[CategoryFee.Senior]);
    }
}
This would produce:
Indexer
Topics on Indexed Properties

 
Multi-Parameterized Indexed Properties

The indexed properties we have used so far were taking only one parameter. You can create an indexed property whose array uses more than one dimension. To start an indexed property that would use various parameters, first declare the array. Then create a this property that takes the parameters. Here is an example for an indexed property that relates to a two-dimensional array:
public class Numbers
{
    double[,] nbr;

    public double this[int x, int y]
    {
    }
}
In the body of an accessor (get or set), use the parameter as appropriately as you see fit. At a minimum, for a get accessor, you can return the value of the array using the parameters based on the rules of a two-dimensional array. This can be done as follows:
public class Numbers
{
    double[,] nbr;

    public double this[int x, int y]
    {
        get { return nbr[x, y]; }
    }
}
After creating the property, you can access each element of the array by applying the square brackets to an instance of the class. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Numbers
{
    double[,] nbr;

    public double this[int x, int y]
    {
        get { return nbr[x, y]; }
    }

    public Numbers()
    {
        nbr = new double[2, 4];
        nbr[0, 0] = 927.93;
        nbr[0, 1] = 45.155;
        nbr[0, 2] = 2.37094;
        nbr[0, 3] = 73475.25;
        nbr[1, 0] = 186.72;
        nbr[1, 1] = 82.350;
        nbr[1, 2] = 712734.95;
        nbr[1, 3] = 3249.0057;
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var nbr = new Numbers();

        Response.Write("<bre>");
        for (int i = 0; i < 2; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                double value = nbr[i, j];
                Response.Write("Number [" + i + "][" + j + "]: " + value + "<br />");
            }
        }
        Response.Write("</bre>");
    }
}
Remember that one of the most valuable features of an indexed property is that, when creating it, you can make it return any primitive type and you can make it take any parameter of your choice. Also, the parameters of a multi-parameter indexed property don't have to be the same type. One can be a character while the other is a bool type; one can be a double while the other is a short, one can be an integer while the other is a string.
When defining the property, you must apply the rules of both the methods and the arrays. Here is an example of a property that takes an integer and a string:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Catalog
{
    long[] nbrs;
    string[] names;

    public double this[long nbr, string name]
    {
        get
        {
            if ((nbr == nbrs[0]) && (name == names[0]))
                return 275.25;
            else if ((nbr == nbrs[1]) && (name == names[1]))
                return 18.75;
            else if ((nbr == nbrs[2]) && (name == names[2]))
                return 50.00;
            else if ((nbr == nbrs[3]) && (name == names[3]))
                return 65.35;
            else if ((nbr == nbrs[4]) && (name == names[4]))
                return 25.55;
            else
                return 0.00;
        }
    }

    public Catalog()
    {
        nbrs = new long[5];
        nbrs[0] = 273974;
        nbrs[1] = 539759;
        nbrs[2] = 710234;
        nbrs[3] = 220685;
        nbrs[4] = 192837;
        names = new string[5];
        names[0] = "Women Double-faced wool coat";
        names[1] = "Men Cotton Polo Shirt";
        names[2] = "Children Cable-knit Sweater";
        names[3] = "Women Floral Silk Tank Blouse";
        names[4] = "Girls Jeans with Heart Belt";
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var cat = new Catalog();

        var itemNumber = 539759;
        var itemDescription = "Men Cotton Polo Shirt";
        var price = cat[itemNumber, itemDescription];

        Response.Write("<pre>");
        Response.Write("Item #:      " + itemNumber + "<br />");
        Response.Write("Description: " + itemDescription + "<br />");
        Response.Write("Unit Price:  " + price + "<br />");
        Response.Write("</pre>");
    }
}
This would produce:
Indexer
In the above example, we first declared the variables to be passed as parameters to the indexed property. You can also pass the parameter(s) directly on the instance of the class. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Catalog
{
    long[] nbrs;
    string[] names;

    public double this[long nbr, string name]
    {
        get
        {
            if ((nbr == nbrs[0]) && (name == names[0]))
                return 275.25;
            else if ((nbr == nbrs[1]) && (name == names[1]))
                return 18.75;
            else if ((nbr == nbrs[2]) && (name == names[2]))
                return 50.00;
            else if ((nbr == nbrs[3]) && (name == names[3]))
                return 65.35;
            else if ((nbr == nbrs[4]) && (name == names[4]))
                return 25.55;
            else
                return 0.00;
        }
    }

    public Catalog()
    {
        nbrs = new long[5];
        nbrs[0] = 273974;
        nbrs[1] = 539759;
        nbrs[2] = 710234;
        nbrs[3] = 220685;
        nbrs[4] = 192837;
        names = new string[5];
        names[0] = "Women Double-faced wool coat";
        names[1] = "Men Cotton Polo Shirt";
        names[2] = "Children Cable-knit Sweater";
        names[3] = "Women Floral Silk Tank Blouse";
        names[4] = "Girls Jeans with Heart Belt";
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var cat = new Catalog();

        var price = cat[220685, "Women Floral Silk Tank Blouse"];

        Response.Write("<pre>");
        Response.Write("Item #:      220685<br />");
        Response.Write("Description: Women Floral Silk Tank Blouse<br />");
        Response.Write("Unit Price:  " + price + "<br /></pre>");
    }
}
Indexed Properties
Just as you can create a two-dimensional indexed property, you can also create a property that takes more than two parameters. Once again, it is up to you to decide what type of parameter would be positioned where in the square brackets. Here is an example of an indexed property that takes three parameters:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Catalog
{
    public string this[long nbr, string name, double price]
    {
        get
        {
            return "Item #:      " + nbr.ToString() + "\n" +
                   "Description: " + name + "\n" +
                   "Unit Price:  " + price.ToString("C");
        }
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var cat = new Catalog();

        Response.Write("<pre>Item Description<br />");
        Response.Write(cat[220685, "Women Floral Silk Tank Blouse", 50.00] + "</pre>");
    }
}
Indexer
Overloading an Indexed Property

To overload the this property, if two indexed properties take only one parameter, each must take a different (data) type of parameter than the other. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class StudentIdentifications
{
    int[] studentIDs;
    string[] fullnames;

    // This property takes a student ID, as an integer,
    // and it produces his/her name
    public string this[int id]
    {
        get
        {
            for (int i = 0; i < studentIDs.Length; i++)
                if (id == studentIDs[i])
                    return fullnames[i];

            return "Unknown Student";
        }
    }

    // This property takes a student name, as a string,
    // and it produces his/her student ID
    public int this[string name]
    {
        get
        {
            for (int i = 0; i < fullnames.Length; i++)
                if (name == fullnames[i])
                    return studentIDs[i];

            return 0;
        }
    }

    public StudentIdentifications()
    {
        studentIDs = new int[6];
        studentIDs[0] = 39472;
        studentIDs[1] = 13957;
        studentIDs[2] = 73957;
        studentIDs[3] = 97003;
        studentIDs[4] = 28947;
        studentIDs[5] = 97395;

        fullnames = new string[6];
        fullnames[0] = "Paul Bertrand Yamaguchi";
        fullnames[1] = "Ernestine Ngovayang";
        fullnames[2] = "Patricia L Katts";
        fullnames[3] = "Helene Mukoko";
        fullnames[4] = "Joan Ursula Hancock";
        fullnames[5] = "Arlette Mimosa";
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var std = new StudentIdentifications();

        Response.Write("<pre>Student Identification<br />");
        Response.Write("Student ID: 39472<br />");
        Response.Write("Full Name:  " + std[39472]);
        
        Response.Write("<p></p>");

        Response.Write("<br />Student Identification");
        Response.Write("<br />Full Name:  Joan Ursula Hancock");
        Response.Write("<br />Student ID: " + std["Joan Ursula Hancock"] + "<pre>");
    }
}
 

  


 

This would produce:
Overloading

 Borrowing the features of a method, an indexer can take one or more parameters and it can return a value. Besides passing different types of parameters to various indexers, you can create some of them that take more than one parameter. 
 

 
Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Catalog
{
    //double[] item;
    long[] nbrs;
    string[] names;
    double[] prices;

    // This property produces the name of an item, as a string,
    // if it is given the item #, as a number
    public string this[long nbr]
    {
        get
        {
            for (int i = 0; i < nbrs.Length; i++)
                if (nbr == nbrs[i])
                    return names[i];

            return "Unknown Item";
        }
    }

    // This property produces the price of the item, as a number,
    // if it is given the item name and its number
    public double this[string name, long nbr]
    {
        get
        {
            for (int i = 0; i < 5; i++)
                if ((nbr == nbrs[i]) && (name == names[i]))
                    return prices[i];

            return 0.00;
        }
    }

    public Catalog()
    {
        nbrs = new long[5];
        nbrs[0] = 273974;
        nbrs[1] = 539759;
        nbrs[2] = 710234;
        nbrs[3] = 220685;
        nbrs[4] = 192837;

        names = new string[5];
        names[0] = "Women Double-faced wool coat";
        names[1] = "Men Cotton Polo Shirt";
        names[2] = "Children Cable-knit Sweater";
        names[3] = "Women Floral Silk Tank Blouse";
        names[4] = "Girls Jeans with Heart Belt";

        prices = new double[5];
        prices[0] = 275.25;
        prices[1] = 18.75;
        prices[2] = 50.00;
        prices[3] = 65.35;
        prices[4] = 25.55;
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var cat = new Catalog();

        Response.Write("<pre>Item Identification<br />");
        Response.Write("Item #:      539759<br />");
        Response.Write("Unit Price:  " + cat[539759]);
        
        Response.Write("<p></p>");

        Response.Write("<br />Item Identification");
        Response.Write("<br />Item #:      192837");
        Response.Write("<br />Description: Girls Jeans with Heart Belt");
        Response.Write("<br />Unit Price:  " +
            cat["Girls Jeans with Heart Belt", 192837] + "<pre>");
    }
}

This would produce:
Overloading


Read/Write Indexed Properties

 

Introduction

So far, we have purposely used indexed properties that only produced a value. If you want an indexed property to be read/write, besides the get accessor, you should also include a set accessor.

A Read/Write Property of a Primitive Type

To create a read/write indexed property, you should include a set accessor for the property. In the set accessor, assign the value contextual keyword to the field indexed with the this parameter. Here is an example of a read/write indexed property that includes a set accessor:
public class Number
{
    double[] numbers;

    public double this[int i]
    {
        get { return numbers[i]; }
        set { numbers[i] = value; }
    }
}

After creating the read/write property, you can assign its values outside of the class. In other words, clients of the class can change the values to its elements. Here is an example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Number
{
    double[] numbers = new double[5];

    public double this[int i]
    {
        get { return numbers[i]; }
        set { numbers[i] = value; }
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var nbr = new Number();

        nbr[2] = 2.37094;

        Response.Write("<pre>");
        for (int i = 0; i < 5; i++)
            Response.Write("Number " + (i + 1) + ": " + nbr[i] + "<br />");
        Response.Write("<pre>");
    }
}

This would produce:
Read-Write Property
Based on this, a type of formula to create and use a basic indexed property is:
class ClassName
{
    DataType[] ArrayName = new DataType[Index];

    public DataType this[int i]
    {
        get { return ArrayName[i]; }
        set { ArrayName[i] = value; }
    }
}

We saw that the index of a property could be a value other than an integer-based. For example, we created an index that was a string type. For such a property, if you make it read/write, you can assign its values outside of the class. Here is an example of a read/write string-based indexed 

property:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public class Philosopher
{
    string[] phil = new string[8];

    public string this[int i]
    {
        get { return phil[i]; }
        set { phil[i] = value; }
    }
}

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var thinker = new Philosopher();

        thinker[5] = "Stuart Rachels";

        Response.Write("<pre>");
        for (int i = 0; i < 8; i++)
            Response.Write("Philosopher: " + thinker[i] + "<br />");
        Response.Write("</pre>");
    }
}

This would produce:
Read-Write Property
The same rules would apply to a read/write indexed property that can receive Boolean or decimal values.

No comments :