This lesson teaches C# Indexers. Our objectives are as follows:
- Understand What Indexers Are For.
- Implement an Indexer.
- Overload Indexers.
- Understand How to Implement Multi-Parameter Indexers.
Indexers allow your class to be used just like an array. On the inside of a class,
you manage a collection of values any way you want. These objects could be a finite
set of class members, another array, or some complex data structure. Regardless
of the internal implementation of the class, its data can be obtained consistently
through the use of indexers. Here's an example.
Listing 11-1. An Example of An Indexer: IntIndexer.cs
using System;
///
<summary>
/// A simple indexer example.
///
</summary>
class IntIndexer
{
private
string[] myData;
public IntIndexer(int
size)
{
myData = new string[size];
for (int i=0; i < size; i++)
{
myData[i] = "empty";
}
}
public
string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] =
value;
}
}
static
void Main(string[] args)
{
int size = 10;
IntIndexer myInd =
new IntIndexer(size);
myInd[9] = "Some Value";
myInd[3] = "Another Value";
myInd[5] = "Any Value";
Console.WriteLine("\nIndexer Output\n");
for (int i=0; i < size; i++)
{
Console.WriteLine("myInd[{0}]:
{1}", i, myInd[i]);
}
}
}
Listing 11-1 shows how to implement an Indexer. The
IntIndexer class has
a
string array named
myData. This is a private array that external
users can't see. This array is initialized in the constructor, which accepts an
int size parameter, instantiates the
myData array, and then
fills each element with the word "empty".
The next class member is the Indexer, which is identified by the
this keyword
and square brackets,
this[int pos]. It accepts a single position parameter,
pos. As you may have already guessed, the implementation of an Indexer is
the same as a Property. It has
get and
set accessors that are used
exactly like those in a Property. This indexer returns a
string, as indicated
by the
string return value in the Indexer declaration.
The
Main() method simply instantiates a new
IntIndexer object, adds
some values, and prints the results. Here's the output:
Indexer Output
myInd[0]: empty
myInd[1]: empty
myInd[2]: empty
myInd[3]: Another Value
myInd[4]: empty
myInd[5]: Any Value
myInd[6]: empty
myInd[7]: empty
myInd[8]: empty
myInd[9]: Some Value
Using an
integer is a common means of accessing arrays in many languages,
but the C# Indexer goes beyond this. Indexers can be declared with multiple parameters
and each parameter may be a different type. Additional parameters are separated
by commas, the same as a method parameter list. Valid parameter types for Indexers
include
integers,
enums, and
strings. Additionally, Indexers
can be overloaded. In listing 11-2, we modify the previous program to accept overloaded
Indexers that accept different types.
Listing 11-2. Overloaded Indexers: OvrIndexer.cs
using System;
///
<summary>
/// Implements overloaded
indexers.
///
</summary>
class OvrIndexer
{
private
string[] myData;
private
int
arrSize;
public OvrIndexer(int
size)
{
arrSize = size;
myData = new string[size];
for (int i=0; i < size; i++)
{
myData[i] = "empty";
}
}
public
string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] =
value;
}
}
public
string this[string data]
{
get
{
int count = 0;
for (int i=0; i <
arrSize; i++)
{
if (myData[i] == data)
{
count++;
}
}
return count.ToString();
}
set
{
for (int i=0; i < arrSize; i++)
{
if (myData[i] == data)
{
myData[i] = value;
}
}
}
}
static
void Main(string[] args)
{
int size = 10;
OvrIndexer myInd =
new OvrIndexer(size);
myInd[9] = "Some Value";
myInd[3] = "Another Value";
myInd[5] = "Any Value";
myInd["empty"] = "no value";
Console.WriteLine("\nIndexer Output\n");
for (int i=0; i < size; i++)
{
Console.WriteLine("myInd[{0}]:
{1}", i, myInd[i]);
}
Console.WriteLine("\nNumber of \"no value\"
entries: {0}", myInd["no value"]);
}
}
Listing 11-2 shows how to overload Indexers. The first Indexer, with the
int
parameter,
pos, is the same as in Listing 11-1, but there is a new Indexer
that takes a
string parameter. The
get accessor of the new indexer
returns a
string representation of the number of items that match the parameter
value,
data. The
set accessor changes each entry in the array that
matches the
data parameter to the value that is assigned to the Indexer.
The behavior of the overloaded Indexer that takes a
string parameter is demonstrated
in the
Main() method of Listing 11-2. It invokes the
set accessor,
which assigns the value of "no value" to every member of the
myInd class
that has the value of "empty". It uses the following command:
myInd["empty"] = "no
value";. After each entry of the
myInd class is printed, a final
entry is printed to the console, indicating the number of entries with the "no value"
string. This happens by invoking the
get accessor with the following code:
myInd["no value"]. Here's the output:
Indexer Output
myInd[0]: no value
myInd[1]: no value
myInd[2]: no value
myInd[3]: Another Value
myInd[4]: no value
myInd[5]: Any Value
myInd[6]: no value
myInd[7]: no value
myInd[8]: no value
myInd[9]: Some Value
Number of "no value" entries: 7
The reason both Indexers in Listing 11-2 can coexist in the same class is because
they have different signatures. An Indexer signature is specified by the number
and type of parameters in an Indexers parameter list. The class will be smart enough
to figure out which Indexer to invoke, based on the number and type of arguments
in the Indexer call. An indexer with multiple parameters would be implemented something
like this:
- public object
this[int param1, ...,
int paramN]
{
get
{
// process and return some class data
}
set
{
// process and
assign some class data
}
}
Summary
You now know what Indexers are for and how they're used. You can create an Indexer
to access class members similar to arrays. Overloaded and multi-parameter Indexers
were also covered.
No comments:
Post a Comment