C# - Position Based and Value Based Indexer Explained With Example

Updated on November 19, 2018
sirama profile image

I am a software engineer. I have been working with C++, MFC, and .net technologies for 15 years. I like video games and reading books.

1. Introduction

We all know Array is nothing but sequential memory locations in which it stores data. Let us say the size of continues memory location is 80 KB and size of one unit of data is 2 KB. The statement implies that we have an array of 40 data in a sequential memory locations. The below picture explains this:

Blocks of Memory
Blocks of Memory | Source

For Example, Consider the below Array:

Department dpt = new Department[40];

If we assume the size required to store each department is 2 KB, we have 40 blocks of size 2 KB is allocated to accommodate 40 department objects. Also, note that 40 objects are allocated in sequential order. So, how do we get the object at the third memory block? We use the below statement:

Dpt[2];

What is [2] represents here? It says to take the object from the third memory block. So here, each memory blocks are referred by the Indexed location. So the notation [] is what called Indexer.

In this article, we will create a collection class and then we will see how we can implement a simple Position Based Indexer and Value Based Indexer.

2. The Product Class

We consider the below specified simple class which represents the product for a retail shop. It has two private data members, a constructor and a public methods to set or retrieve the data members.

//001: Product Class. 
public class Product
{
	private int ProductId;
	private string ProductName;

	public Product(int id, string Name)
	{
		ProductId = id;
		ProductName = Name;
	}

	public string GetProdName()
	{
		return ProductName;
	}
}

3. The SuperMarket Class

As Every Super market have a collection of products, this class will have a collection of a product object. The members of this class is shown below:

//002: SuperMarket has collection of products. 
//It implements Indexers.
public class SuperMarketX
{
    //002_1: Declaration
    private int pos;
    private string shopname;
    private Product[] Products;
    //0-Position based index. 1-Value based Index.
    public int numeric_index_mode; 

The variable “Pos” is to iterate through the Products collection. OK, you may get the idea now. The class SuperMarket is a user defined (defined by us now) collection of Products.

The constructor of this class will take an array of products as a parameter and assigns it to the private member of the Products instance. Note, for this article, we are allocating fixed space of 1000 slots and each space has null reference initially. We will replace the null reference with the passed in the array of objects. Below is the code for the Constructor:

//002_2: Constructor
public SuperMarketX(string shopname, 
    params Product[] products)
{
    //002_2.1: Allocate the Space required
    this.Products = new Product[1000];
    pos = 0;

    //002_2.2: first set null to all the elements
    for (int i=0; i< 1000; i++)
        Products[i] = null;

    //002_2.3: Assign the Array by taking the references 
    //from incoming array. The reference will replace 
    //the previous null assignment
    foreach (Product prd in products)
    {
        Products[pos] = prd;
        pos++;
    }

    //002_2.4: Set the Shop Name and Index
    this.shopname = shopname;
    numeric_index_mode = 0;
}

We override the ToString() method to get the entire product in a comma-separated format. The method implementation is shown below:

//004: Override the ToString to 
//display all the Product Names as 
//Comma Separated List
public override string ToString()
{
    string returnval = "";
    foreach (Product p in Products )
    {
        if ( p != null )
            returnval = returnval + "," + p.GetProdName();
    }
    //Cut the leading "," and return
    return returnval.Substring(1, returnval.Length-1 );
}

4. Position based Indexer

The will implement the indexer just like the operator overloading functions. To implement the '[]’ notation follow the below Syntax:

Syntax of C# Indexer
Syntax of C# Indexer | Source

The Implementation Skeleton on the Simple Indexer is shown below:

Position Based Indexer
Position Based Indexer | Source

In the above picture, we can see that get portion of the indexer is called whenever we want to read from the collection using “Index Of” operator. The same way, set portion gets called when we want to write to the collection.

In our case, we will implement the Index for the Supermarket. So, using the Positional Index, we will retrieve a product. The way the index implemented will give a NULL reference to the caller when the index is out of Range Say below 0 or above 1000. Note, the Maximum product supported by the supermarket is 1000. Below is the function implementation:

//003: The Use of Indexer. Positional Indexer
public Product this[int index]
{
    get
    {
        //003_1: Retrieve value based on 
        //positional index
        if (index >= Products.Length || index < 0)
        {
            return null;
        }
        return Products[index];
    }
    set
    {
        //003_2: Set the value based on the 
        //positional index
        if (index >= Products.Length)
        {
            return;
        }
        Products[index] = value;
    }
}

The client code which uses the indexer is given below.

//Client 001: First Let us create an array 
//to hold 6 Products. 
Product[] theProdArray = new Product[6];

//Client 002: Create 6 individual Product and 
//store it in the array
theProdArray[0] = new Product(1001, "Beer");
theProdArray[1] = new Product(1002, "Soda");
theProdArray[2] = new Product(1003, "Tea");
theProdArray[3] = new Product(1004, "Coffee");
theProdArray[4] = new Product(1005, "Apple");
theProdArray[5] = new Product(1006, "Grapes");

//Client 003: Super Market that holds six 
//product collection
SuperMarketX market = 
    new SuperMarketX("Z Stores", theProdArray);
Console.WriteLine(
    "Product Available in Super Market: " 
    + market );

//Client 004: Use the Simple 
//Indexer to Assign the value
market[15] = new Product(1015, "Orange");
Console.WriteLine(
    "Product Available in Super Market: " 
    + market );

//Client 005: Use the Simple Indexer to 
//retrieve the value
Product prod = market[5];
Console.WriteLine("The product retrieved is: " 
    + prod.GetProdName() );

Code Explanation

  1. Client 001: Creates the Array of 6 Products.
  2. Client 002: Populates the product array. In real world Array will be populated from Database.
  3. Client 003: Supermarket is created with 6 New Products. Note, in our example, the supermarket capacity is 1000.
  4. Client 004: Uses the Indexer to add a new product to the Products collection. market[15] = new Product(1015, "Orange"); Will call the indexer with index = 15. new Product(1015, "Orange"); will be referred in the set portion of our Indexer using the value keyword.
  5. Client 005: Product prod = market[5]; Supermarket Object accessed with Indexer [5]. We will move to get a portion of the Indexer and indexer returns Product at the position offset 5. The returned object reference is assigned to prod.

5. Value Based Indexer

The previous indexer locates the memory block based on the Index by calculating the offset as it knows the size of the memory block. Now, we will implement value-based index which will get the product based on the ProductId value. We will walk through the changes done on the Classes.

1) The product class changed to have a method which sets the ProductName, and a get method for ProductId. We also have an overridden method for ToString just to print Product Name. Below are the Changes:

public override string ToString()
{
            return ProductName;
}

public int GetProductId()
{
            return ProductId;
}

public void SetProductName(string newName)
{         
            ProductName = newName;
}

2) In the SuperMarket class, we declare a variable called numeric_index_mode. We use this variable to decide whether the Indexer is referred as Positional-based or in Value-based .

//0-Position based index. 1-Value based Index.
public int numeric_index_mode; 

Inside the constructor, We initialize indexer mode to 0. It means, the SuperMarket class by default treats the Indexer as Positional indexer and retrieves the product based on the calculated positional offset.

numeric_index_mode = 0;

3) We implement a public function to retrieve the Positional index for the passed-in Product Id. Note, the product id is unique for this Value based Index. The function will iterate through the Products in the Supermarket and returns when a match for Product ID is found. It will return –1 when match not occurred. Below is the new function implemented to support the value-based index:

//005: Supporting function for value based Index
public int GetProduct(int Productid)
{
	for (int i = 0; i < Products.Length; i++)
	{
		Product p = Products[i];
		if ( p != null )
		{
			int prodid = p.GetProductId();
			if (prodid == Productid)
				return i;
		}
	}
	return -1;
}

4) First, in the get portion of the Indexer, wrap the existing code with an if construct. That is; when the Mode = 0, go with positional Index. It holds true for Set portion of the Indexer as well. Below is the Change:

public Product this[int index]
{
    get
    {
        //003_1: Retrieve Product based on 
		//positional index
        if (numeric_index_mode == 0)
        {
            if (index >= Products.Length || index < 0 ) 
            {
                return null;
            }
            return Products[index];
        }
	
        //003_3: Other Index modes are Skipped 
        //or Not Implemented
        return null;
    }
    set
    {
        //003_2: Set the value based on the 
		//positional index
        if (numeric_index_mode == 0 )
        {
            if (index >= Products.Length )
            {
                return;
            }
            Products[index] = value ;
        }
    }
}

5) If we are in Value mode, In the Get part of the indexer first get the positional index for a product id. Once we have the positional index, we are ready to make a recursive call to same indexer routine. Make sure to set the indexer mode to 0 as we need to access the indexer to get the product based on the indexed position. Once we have the Product, reset index mode back to 1; that reset indexer mode to value based on the client code would expect that. Below is the Code for “Get” portion:

//003_2: Retrieve Product based on the Unique product Id
if(numeric_index_mode == 1)
{
    int idx = GetProduct(index);
    if (idx == -1)
        return null;
    else
    {
        //Key statement to avoid recursion
        numeric_index_mode = 0;
        //Recursive call to Indexer
        Product ret_Product = this[idx];
        //Reset it back to user preference
        numeric_index_mode = 1; 
        return ret_Product;
    }

Note, we can change the GetProduct function to return a product and make this implementation simple.

6) Set portion of the Indexer also changed in the same way. I hope further explanation not required:

//003_3: Set the value based on the Id Passed in.
if(numeric_index_mode == 1)
{
    int idx = GetProduct(index);
    if (idx == -1)
        return ;
    else
    {
        //Key statement to avoid recursion
        numeric_index_mode = 0;
        Products[idx] = value;
        //Reset it back to user preference
        numeric_index_mode = 1; 
    }
}

Using Value based Indexer

Code below explains how we switch from Position based indexer to Value based indexer, use value based indexer and go back to default indexer mode. Read the inline comments and it is easy to follow.

//=====> Value based Index     <=======
//Now we will operate on the Value based Index
market.numeric_index_mode = 1;
//Client 006: Display name of the product 
//whose product id is 1005
Console.WriteLine("Name of the Product" +
    "represented by Id 1005 is: {0}", 
    market[1005]);

//Client 007: The aim is Replace the Product 
//Soda with Iced Soda and maintain same product id. 
//The Id of Soda is 1002.
if (market[1002] != null )
{
    market[1002].SetProductName("Iced Soda");
    Console.WriteLine("Product Available in " +
        "Super Market: " + market );
}

//Client 008: Remove Tea and Add French Coffee. 
//Note the Object in the Indexed location will 
//be changed.
//Note: Here check for the null is not required. 
//Kind of Modify on fail Add
market[1003] = new Product(1007, "French Coffee");
Console.WriteLine("Product Available in " +
    "Super Market: " + market );

//Reset back to Standard Positional Index    
market.numeric_index_mode = 0;
//Dot

6. Closing Notes

1) You can implement string value based indexer also. The skeleton is:

public Product this[string ProductName]
{
    Set{}
    Get{}
}

Complete Source Code

Indexer.cs

using System;

namespace _005_Indexers
{

	//001: Product Class. 
	public class Product
	{
		private int ProductId;
		private string ProductName;

		public Product(int id, string Name)
		{
			ProductId = id;
			ProductName = Name;
		}

		public string GetProdName()
		{
			return ProductName;
		}

		public override string ToString()
		{
			return ProductName;
		}

		public int GetProductId()
		{
			return ProductId;
		}

		public void SetProductName(string newName)
		{	
			ProductName = newName;
		}
	}


	//002: SuperMarket has collection of products. It implements Indexers.
	public class SuperMarketX
	{
		//002_1: Declaration
		private int pos;
		private string shopname;
		private Product[] Products;
        //0-Position based index. 1-Value based Index.
        public int numeric_index_mode; 

		//002_2: Constructor
		public SuperMarketX(string shopname, params Product[] products)
		{
			//002_2.1: Allocate the Space required
			this.Products = new Product[1000];
			pos = 0;

			//002_2.2: first set null to all the elements
			for (int i=0; i< 1000; i++)
				Products[i] = null;

			//002_2.3: Assign the Array by taking the references from incoming array. 
			//				The reference will replace the previous null assignment
			foreach (Product prd in products)
			{
				Products[pos] = prd;
				pos++;
			}

			//002_2.4: Set the Shop Name and Index
			this.shopname = shopname;
			numeric_index_mode = 0;
		}

		//003: The Use of Indexer. Positional Indexer
        public Product this[int index]
        {
            get
            {
                //003_1: Retrieve Product based on positional index
                if (numeric_index_mode == 0)
                {
                    if (index >= Products.Length || index < 0 ) 
                    {
                        return null;
                    }
                    return Products[index];
                }

                //003_2: Retrieve Product based on the Unique product Id
                if(numeric_index_mode == 1)
                {
                    int idx = GetProduct(index);
                    if (idx == -1)
                        return null;
                    else
                    {
                        //Key statement to avoid recursion
                        numeric_index_mode = 0;
                        //Recursive call to Indexer
                        Product ret_Product = this[idx];
                        //Reset it back to user preference
                        numeric_index_mode = 1; 
                        return ret_Product;
                    }
                }
        		
                //003_3: Other Index modes are Skipped or Not Implemented
                return null;
            }
            set
            {
                //003_2: Set the value based on the positional index
                if (numeric_index_mode == 0 )
                {
                    if (index >= Products.Length )
                    {
                        return;
                    }
                    Products[index] = value ;
                }

                //003_3: Set the value based on the Id Passed in.
                if(numeric_index_mode == 1)
                {
                    int idx = GetProduct(index);
                    if (idx == -1)
                        return ;
                    else
                    {
                        //Key statement to avoid recursion
                        numeric_index_mode = 0;
                        Products[idx] = value;
                        //Reset it back to user preference
                        numeric_index_mode = 1; 
                    }
                }
            }
        }

		//004: Override the ToString to display all the Product Names as Comma Separated List
		public override string ToString()
		{
			string returnval = "";
			foreach (Product p in Products )
			{
				if ( p != null )
					returnval = returnval + "," + p.GetProdName();
			}
			//Cut the leading "," and return
			return returnval.Substring(1, returnval.Length-1 );
		}

        //005: Supporting function for value based Index
        public int GetProduct(int Productid)
        {
	        for (int i = 0; i < Products.Length; i++)
	        {
		        Product p = Products[i];
		        if ( p != null )
		        {
			        int prodid = p.GetProductId();
			        if (prodid == Productid)
				        return i;
		        }
	        }
	        return -1;
        }
	}

	class ProgramEntry
	{
		[STAThread]
		static void Main(string[] args)
		{
            //Client 001: First Let us create an array 
            //to hold 6 Products. 
            Product[] theProdArray = new Product[6];

            //Client 002: Create 6 individual Product and 
            //store it in the array
            theProdArray[0] = new Product(1001, "Beer");
            theProdArray[1] = new Product(1002, "Soda");
            theProdArray[2] = new Product(1003, "Tea");
            theProdArray[3] = new Product(1004, "Coffee");
            theProdArray[4] = new Product(1005, "Apple");
            theProdArray[5] = new Product(1006, "Grapes");

            //Client 003: Super Market that holds six 
            //product collection
            SuperMarketX market = 
                new SuperMarketX("Z Stores", theProdArray);
            Console.WriteLine(
                "Product Available in Super Market: " 
                + market );

            //Client 004: Use the Simple 
            //Indexer to Assign the value
            market[15] = new Product(1015, "Orange");
            Console.WriteLine(
                "Product Available in Super Market: " 
                + market );

            //Client 005: Use the Simple Indexer to 
            //retrieve the value
            Product prod = market[5];
            Console.WriteLine("The product retrieved is: " 
                + prod.GetProdName() );

            //=====> Value based Index     <=======
            //Now we will operate on the Value based Index
            market.numeric_index_mode = 1;
            //Client 006: Display name of the product 
            //whose product id is 1005
            Console.WriteLine("Name of the Product" +
                "represented by Id 1005 is: {0}", 
                market[1005]);

            //Client 007: The aim is Replace the Product 
            //Soda with Iced Soda and maintain same product id. 
            //The Id of Soda is 1002.
            if (market[1002] != null )
            {
                market[1002].SetProductName("Iced Soda");
                Console.WriteLine("Product Available in " +
                    "Super Market: " + market );
            }

            //Client 008: Remove Tea and Add French Coffee. 
            //Note the Object in the Indexed location will 
            //be changed.
            //Note: Here check for the null is not required. 
            //Kind of Modify on fail Add
            market[1003] = new Product(1007, "French Coffee");
            Console.WriteLine("Product Available in " +
                "Super Market: " + market );

            //Reset back to Standard Positional Index    
            market.numeric_index_mode = 0;
            //Dot
		}
	}
}

The Code output

The output of executing the above example is given below:

Position and Value based indexer output
Position and Value based indexer output | Source

Questions & Answers

    Comments

      0 of 8192 characters used
      Post Comment

      No comments yet.

      working

      This website uses cookies

      As a user in the EEA, your approval is needed on a few things. To provide a better website experience, owlcation.com uses cookies (and other similar technologies) and may collect, process, and share personal data. Please choose which areas of our service you consent to our doing so.

      For more information on managing or withdrawing consents and how we handle data, visit our Privacy Policy at: https://owlcation.com/privacy-policy#gdpr

      Show Details
      Necessary
      HubPages Device IDThis is used to identify particular browsers or devices when the access the service, and is used for security reasons.
      LoginThis is necessary to sign in to the HubPages Service.
      Google RecaptchaThis is used to prevent bots and spam. (Privacy Policy)
      AkismetThis is used to detect comment spam. (Privacy Policy)
      HubPages Google AnalyticsThis is used to provide data on traffic to our website, all personally identifyable data is anonymized. (Privacy Policy)
      HubPages Traffic PixelThis is used to collect data on traffic to articles and other pages on our site. Unless you are signed in to a HubPages account, all personally identifiable information is anonymized.
      Amazon Web ServicesThis is a cloud services platform that we used to host our service. (Privacy Policy)
      CloudflareThis is a cloud CDN service that we use to efficiently deliver files required for our service to operate such as javascript, cascading style sheets, images, and videos. (Privacy Policy)
      Google Hosted LibrariesJavascript software libraries such as jQuery are loaded at endpoints on the googleapis.com or gstatic.com domains, for performance and efficiency reasons. (Privacy Policy)
      Features
      Google Custom SearchThis is feature allows you to search the site. (Privacy Policy)
      Google MapsSome articles have Google Maps embedded in them. (Privacy Policy)
      Google ChartsThis is used to display charts and graphs on articles and the author center. (Privacy Policy)
      Google AdSense Host APIThis service allows you to sign up for or associate a Google AdSense account with HubPages, so that you can earn money from ads on your articles. No data is shared unless you engage with this feature. (Privacy Policy)
      Google YouTubeSome articles have YouTube videos embedded in them. (Privacy Policy)
      VimeoSome articles have Vimeo videos embedded in them. (Privacy Policy)
      PaypalThis is used for a registered author who enrolls in the HubPages Earnings program and requests to be paid via PayPal. No data is shared with Paypal unless you engage with this feature. (Privacy Policy)
      Facebook LoginYou can use this to streamline signing up for, or signing in to your Hubpages account. No data is shared with Facebook unless you engage with this feature. (Privacy Policy)
      MavenThis supports the Maven widget and search functionality. (Privacy Policy)
      Marketing
      Google AdSenseThis is an ad network. (Privacy Policy)
      Google DoubleClickGoogle provides ad serving technology and runs an ad network. (Privacy Policy)
      Index ExchangeThis is an ad network. (Privacy Policy)
      SovrnThis is an ad network. (Privacy Policy)
      Facebook AdsThis is an ad network. (Privacy Policy)
      Amazon Unified Ad MarketplaceThis is an ad network. (Privacy Policy)
      AppNexusThis is an ad network. (Privacy Policy)
      OpenxThis is an ad network. (Privacy Policy)
      Rubicon ProjectThis is an ad network. (Privacy Policy)
      TripleLiftThis is an ad network. (Privacy Policy)
      Say MediaWe partner with Say Media to deliver ad campaigns on our sites. (Privacy Policy)
      Remarketing PixelsWe may use remarketing pixels from advertising networks such as Google AdWords, Bing Ads, and Facebook in order to advertise the HubPages Service to people that have visited our sites.
      Conversion Tracking PixelsWe may use conversion tracking pixels from advertising networks such as Google AdWords, Bing Ads, and Facebook in order to identify when an advertisement has successfully resulted in the desired action, such as signing up for the HubPages Service or publishing an article on the HubPages Service.
      Statistics
      Author Google AnalyticsThis is used to provide traffic data and reports to the authors of articles on the HubPages Service. (Privacy Policy)
      ComscoreComScore is a media measurement and analytics company providing marketing data and analytics to enterprises, media and advertising agencies, and publishers. Non-consent will result in ComScore only processing obfuscated personal data. (Privacy Policy)
      Amazon Tracking PixelSome articles display amazon products as part of the Amazon Affiliate program, this pixel provides traffic statistics for those products (Privacy Policy)
      ClickscoThis is a data management platform studying reader behavior (Privacy Policy)