|
|
Getting to Know Indexers | | Many times in your .NET apps, you come across objects that have Properties which aren't scalar values, rather, they are collections.
Take for example a ListView Control. It has an Item property which is a collection of ListViewItems. Each ListViewItem may have ListViewSubIterms as well.
So, if you wanted your class to mimic this functionality, how would you do it? Let's say you have an Employee Class. Each Employee could have zero or more Telephone numbers. So here's a shell of our Employee Class:
namespace Samples
{
/// <summary>
/// Summary description for Employee.
///
public class Employee
{
TelephoneItem _Phones = new TelephoneItem();
public TelephoneItem Phones
{
get{return _Phones;}
set{_Phones = value;}
}
public Employee()
{
//SomeConstructor
}
}
} |
Here's our TelephoneItem Class:
namespace Sample
{
public class TelephoneItem
{
ArrayList _number = new ArrayList();
public TelephoneItem()
{
//SomeConstructor
}
public void Add(string PhoneNumber)
{
_number.Add(PhoneNumber);
}
public object this [int idx]
{
get
{
if(_number.Count < idx)<BR>
{
return _number[idx];
}
else
{
throw new IndexOutOfRangeException("[TelephoneItem.get_Item]" +
"Index Out of Range");
}
}
set
{
if(_number.Count < idx)<BR>
{
_number[idx] = value;
}
else
{
throw new IndexOutOfRangeException("[TelephoneItem.set_Item]" +
"Index Out of Range");
}
}
}
}
}
|
Now, let's call the class and use it:
Employee emp = new Employee();
emp.Phones.Add("305.555.1212");
emp.Phones.Add("412.294.0000");
emp.Phones.Add("423.343.2342");
Debug.WriteLine(emp.Phones[0]); //returns 305.555.1212
Debug.WriteLine(emp.Phones[1]); //returns 412.294.0000 |
With the Phones property, since it's the default property and it's an Indexer, all we need to refer to it is the numeric Index. This is similar to the Item property in Visual Basic .NET, where you can use something like:
myDataReader.Item(0)
myDataReader(0) |
and they will behave identically. This may not seem like a huge deal, but it can definitely add some elegance to your classes. Similarly, you can strongly type your Items collection so you can have the added benefit of using a more object oriented approach as well as having the performance and accuracy associated with Strong Typing. But another benefit is design time support. When you click on the Property Grid in Visual Studio .NET, most of the properties are Scalar, meaning they have only one value. However, Indexed properties will automatically be detected by Visual Studio .NET and provided you strongly typed your items, you will have designer support to add to the collection.
I think Indexers are one of the cooler features I've come across, but I'll caution you about something. When I first learned to use them, I got a little carried away with them and used them everywhere, even when it didn't make sense. The Visual Studio .NET object model is probably one of the best resources to look to when deciding how to model your objects, after all, some of the best minds in OOP worked on its design for quite a while. It makes sense for a ListView to have ListViewItems. A ListView will have 0 or more ListViewItems and they will all be of the same type. Can you think of a better place for a collection? As such, if you have a natural fit, then this is an excellent mechanism to add some elegance to your classes. On the other hand, a TextBox control only has one Text Property, so it would make no sense whatsoever to treat this property as an indexer. Moreover, I can't think of anything that fits the 'indexer' model in a standard textbox. Also, don't confuse indexed properties with enumerated properties. This is a common mistake to make and they are totally different. Using our past example, a ListView has 0 or more ListViewItems. They are the same type. However, look at the CausesValidation property of a textbox. When you look in the designer, it can take one of two values, True or False. This may lead some beginners to believe that this isn't a Scalar property since two things show up in the designer. However, CausesValidation can only be true or false at any given point in time, not both. True and False are merely Enumerations, but they are still members of a Scalar property.
I encourage you to play around with these because they can be quite powerful and are fairly easy to use. Most importantly, they make your classes easier to use, and while you may know the nuances of your classes, chances are others won't, particularly if you primarily write libraries. And since one of the primary goals of OOP is reuse, making your classes easy to use is probably the best way to see to it that they get used and reused. If you had to hand code a ListView each time, you'd probably find yourself using Grids a lot more. The same will hold true for your classes! So do yourself and the consumers of your code a favor, use Indexers when appropriate! |
|