Home

Introduction to Databases:
Indexers

 

Introduction

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. The array can be of a primitive type or composite. Obviously if you want to use a composite type, you can use one of the many built-in .NET Framework classes or you must first create your class. Here is an example:

 
Person.h
#pragma once
using namespace System;

public ref class CPerson
{
public:
    int PersonID;
    String ^ FirstName;
    String ^ LastName;
    String ^ Gender;

public:
    CPerson(void);
    CPerson(int ID, String ^ fn, String ^ ln, String ^ gdr);
};
Person.cpp
#include "StdAfx.h"
#include "Person.h"

CPerson::CPerson(void)
{
}

CPerson::CPerson(int ID, String ^ fn, String ^ ln, String ^ gdr)
{
    PersonID = ID;
    FirstName = fn;
    LastName = ln;
    Gender = gdr;
}

As stated already, to start creating an indexer, declare an array as a field. Here is an example:

Persons.h
#pragma once
#include "Person.h"

ref class CPersons
{
public:
	CPersons(void);

	array<CPerson ^> ^ individual;
};
Persons.cpp
#include "Persons.h"

CPersons::CPersons(void)
{
	individual = gcnew array<CPerson ^>(5);
}

 

In the body of the class, create a property named default that is an array. 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.

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:

#pragma once
#include "Person.h"

ref class CPersons
{
public:
	CPersons(void);

	array<CPerson ^> ^ individual;

	property CPerson ^ default[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:

#pragma once
#include "Person.h"

ref class CPersons
{
public:
	CPersons(void);

	array<CPerson ^> ^ individual;

	property CPerson ^ default[int]
	{
		CPerson ^ get(int i)
		{
			return individual[i];
		}
	}
};

If you want to create an indexer that can only receive values, add a set accessor only. If you want to create an indexed property that can both receive values and provide values, that is, if you want to create a read/write indexed property, create both a get and a set accessors. Here is an example:

#pragma once
#include "Person.h"

ref class CPersons
{
public:
	CPersons(void);

	array<CPerson ^> ^ individual;

	property CPerson ^ default[int]
	{
		CPerson ^ get(int i)
		{
			return individual[i];
		}

		void set(int i, CPerson ^ p)
		{
			individual[i] = p;
		}
	}
};

After creating a read/write indexer, you can assign its values outside of the class. In other words, clients of the class can change the values to its elements. Remember that the advantage of an indexed property is that each element of the arrayed field can be accessed from the instance of the class by directly applying the square brackets and the (appropriate) index to it. Here is an example:

System::Void Exercise_Load(System::Object^  sender, System::EventArgs^  e)
{
    CPersons ^ People = gcnew CPersons;

    People[0] = gcnew CPerson;
    People[0]->PersonID = 72947;
    People[0]->Gender = L"Female";
    People[0]->FirstName = L"Paulette";
    People[0]->LastName = L"Cranston";
            
    People[1] = gcnew CPerson;
    People[1]->PersonID = 70854;
    People[1]->Gender = L"Male";
    People[1]->FirstName = L"Harry";
    People[1]->LastName = L"Kumar";
            
    People[2] = gcnew CPerson;
    People[2]->PersonID = 27947;
    People[2]->Gender = L"Male";
    People[2]->FirstName = L"Jules";
    People[2]->LastName = L"Davidson";
            
    People[3] = gcnew CPerson;
    People[3]->PersonID = 62835;
    People[3]->Gender = L"Unknown";
    People[3]->FirstName = L"Leslie";
    People[3]->LastName = L"Harrington";
            
    People[4] = gcnew CPerson;
    People[4]->PersonID = 92958;
    People[4]->Gender = L"Male";
    People[4]->FirstName = L"Ernest";
    People[4]->LastName = L"Colson";

    for (int i = 0; i < 5; i++ )
    {
	ListViewItem ^ lviPerson = gcnew ListViewItem(People[i]->PersonID.ToString());

	lviPerson->SubItems->Add(People[i]->FirstName);
	lviPerson->SubItems->Add(People[i]->LastName);
	lviPerson->SubItems->Add(People[i]->Gender->ToString());

	lvwPeople->Items->Add(lviPerson);
    }
}

This would produce:

Array

 

 

 

Array-Based and Collection-Based Indexers

When using an indexed property whose field is array-based, you can access only up to the number of elements specified when creating the indexer. In our example, that would be 5. If you try accessing more elements than that, you would receive an IndexOutOfRangeException exception. And because, when using the indexed property, it is treated as a normal class and not an array, you cannot call the Array::Resize() method to increase the number of elements that the variable can hold. If you want to go beyond the number of elements primarily specified, you have various alternatives. You can specify a higher number of elements when creating the indexer. Here is an example:

#include "Persons.h"

CPersons::CPersons(void)
{
	individual = gcnew array<CPerson ^>(100);
}

The disadvantage to this approach is that every time this program runs, it would use more memory than it needs. Of course, this means that it is not a professional technique of solving a problem. An alternative is to create your own means of increasing the number of elements that the variable can hold. Here is an example:

#include "Persons.h"

CPersons::CPersons(void)
{
	individual = gcnew array<CPerson ^>(5);
}

void CPersons::Increase()
{
	Array::Resize<CPerson ^>(individual, individual->Length + 5);
}

After doing this, whenever you know you are about to access an increased number of elements, you can simply apply your means of increasing the size. Here is an example:

System::Void Exercise_Load(System::Object^  sender, System::EventArgs^  e)
{
    CPersons ^ People = gcnew CPersons;

    People[0] = gcnew CPerson;
    People[0]->PersonID = 72947;
    People[0]->Gender = L"Female";
    People[0]->FirstName = L"Paulette";
    People[0]->LastName = L"Cranston";
            
    People[1] = gcnew CPerson;
    People[1]->PersonID = 70854;
    People[1]->Gender = L"Male";
    People[1]->FirstName = L"Harry";
    People[1]->LastName = L"Kumar";
            
    People[2] = gcnew CPerson;
    People[2]->PersonID = 27947;
    People[2]->Gender = L"Male";
    People[2]->FirstName = L"Jules";
    People[2]->LastName = L"Davidson";
            
    People[3] = gcnew CPerson;
    People[3]->PersonID = 62835;
    People[3]->Gender = L"Unknown";
    People[3]->FirstName = L"Leslie";
    People[3]->LastName = L"Harrington";
            
    People[4] = gcnew CPerson;
    People[4]->PersonID = 92958;
    People[4]->Gender = L"Male";
    People[4]->FirstName = L"Ernest";
    People[4]->LastName = L"Colson";

    People->Increase();

    People[5] = gcnew CPerson;
    People[5]->PersonID = 91749;
    People[5]->FirstName = "Patricia";
    People[5]->LastName = "Katts";
    People[5]->Gender = L"Female";

    People[6] = gcnew CPerson;
    People[6]->PersonID = 29749;
    People[6]->FirstName = "Patrice";
    People[6]->LastName = "Abanda";
    People[6]->Gender = L"Unknown";
        
    People[7] = gcnew CPerson;
    People[7]->PersonID = 24739;
    People[7]->FirstName = "Frank";
    People[7]->LastName = "Thomasson";
    People[7]->Gender = L"Male";

    for (int i = 0; i < 8; i++ )
    {
	ListViewItem ^ lviPerson = gcnew ListViewItem(People[i]->PersonID.ToString());

	lviPerson->SubItems->Add(People[i]->FirstName);
	lviPerson->SubItems->Add(People[i]->LastName);
	lviPerson->SubItems->Add(People[i]->Gender->ToString());

	lvwPeople->Items->Add(lviPerson);
    }
}

One more alternative to this problem consists of creating the indexer as a collection.

Multi-Parameterized Indexed Properties

Instead of a single parameter, you can create an indexed property that takes more than one parameter. To start, you can declare the array as a field of a class. After declaring the array, create a default property that takes the parameters. 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. Here is an example for an indexed property that relates to a two-dimensional array:

Header File: Persons.h
#pragma once
#include "Person.h"

ref class CPersons
{
public:
	CPersons(void);

	array<CPerson ^, 2> ^ individual;

	property CPerson ^ default[int, int]
	{
		CPerson ^ get(int i, int j)
		{
			return individual[i, j];
		}

		void set(int i, int j, CPerson ^ p)
		{
			individual[i, j] = p;
		}
	}
};
Source File: Persons.cpp
#include "Persons.h"

CPersons::CPersons(void)
{
	individual = gcnew array<CPerson ^, 2>(2, 4);
}

 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:

System::Void Exercise_Load(System::Object^  sender, System::EventArgs^  e)
{
    CPersons ^ People = gcnew CPersons;

    People[0, 0] = gcnew CPerson;
    People[0, 0]->PersonID = 72947;
    People[0, 0]->Gender = L"Female";
    People[0, 0]->FirstName = L"Paulette";
    People[0, 0]->LastName = L"Cranston";
            
    People[0, 1] = gcnew CPerson;
    People[0, 1]->PersonID = 70854;
    People[0, 1]->Gender = L"Male";
    People[0, 1]->FirstName = L"Harry";
    People[0, 1]->LastName = L"Kumar";
            
    People[0, 2] = gcnew CPerson;
    People[0, 2]->PersonID = 27947;
    People[0, 2]->Gender = L"Male";
    People[0, 2]->FirstName = L"Jules";
    People[0, 2]->LastName = L"Davidson";
            
    People[0, 3] = gcnew CPerson;
    People[0, 3]->PersonID = 62835;
    People[0, 3]->Gender = L"Unknown";
    People[0, 3]->FirstName = L"Leslie";
    People[0, 3]->LastName = L"Harrington";
            
    People[1, 0] = gcnew CPerson;
    People[1, 0]->PersonID = 92958;
    People[1, 0]->Gender = L"Male";
    People[1, 0]->FirstName = L"Ernest";
    People[1, 0]->LastName = L"Colson";

    People[1, 1] = gcnew CPerson;
    People[1, 1]->PersonID = 91749;
    People[1, 1]->FirstName = "Patricia";
    People[1, 1]->LastName = "Katts";
    People[1, 1]->Gender = L"Female";

    People[1, 2] = gcnew CPerson;
    People[1, 2]->PersonID = 29749;
    People[1, 2]->FirstName = "Patrice";
    People[1, 2]->LastName = "Abanda";
    People[1, 2]->Gender = L"Unknown";
        
    People[1, 3] = gcnew CPerson;
    People[1, 3]->PersonID = 24739;
    People[1, 3]->FirstName = "Frank";
    People[1, 3]->LastName = "Thomasson";
    People[1, 3]->Gender = L"Male";

    for (int i = 0; i < 2; i++ )
    {
	for (int j = 0; j < 4; j++)
	{
	    ListViewItem ^ lviPerson = 
		gcnew ListViewItem(People[i, j]->PersonID.ToString());

	    lviPerson->SubItems->Add(People[i, j]->FirstName);
	    lviPerson->SubItems->Add(People[i, j]->LastName);
	    lviPerson->SubItems->Add(People[i, j]->Gender->ToString());

	    lvwPeople->Items->Add(lviPerson);
	}
    }
}

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 do not 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.

Files
Exercise1
Yugo National Bank
 
 
 
   
 

Previous Copyright © 2009-2010 FunctionX, Inc. Home