28

Implementing Indexers in C#

 5 years ago
source link: https://www.tuicool.com/articles/hit/rmaUVru
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Indexers allow instances of a class to be indexed just like arrays, and are declared similarly to methods. They can be useful when working with your own collection class. Let's learn how to define and work with indexers.

You may encounter a situation where you want to access data in your custom types like an array, using the index operator, [] . This feature can be useful when creating custom generic classes. An indexer allows an object to be indexed, such as an array, and the indexed value can be set or retrieved without explicitly specifying a type or instance member.

To declare an indexer for a class, you add a slightly different property with the this[] keyword and arguments of any type between the brackets. Properties return or set a specific data member, whereas indexers return or set a particular value from the object instance.

Let's take a look at an example to see how to implement an indexer.

class User
{
  public string Name { get; set; }
  public string Email { get; set; }
}

class UserCollection
{
  ArrayList users = new ArrayList();

  public User this[int index]
  {
    get => (User) users[index];
    set => users.Insert(index, value);
  }

  public int Count => users.Count;
}

Above we defined an indexer whose argument is an int type. We have used the ArrayList class to hold a collection of User objects and made use of its indexer to retrieve and store users based on the passed value. Here's how to use the indexer:

var users = new UserCollection();

// add objects using indexer
users[0] = new User("Julie Lerman", "[email protected]");
users[1] = new User("Mark Lettuce", "[email protected]");
users[2] = new User("Peter Mbanugo", "[email protected]");

// obtain and display each item using indexer
for (int i = 0; i < users.Count; i++)
{
  Console.WriteLine("User number: {0}", i);
  Console.WriteLine("Name: {0}", users[i].Name);
  Console.WriteLine("Email: {0}", users[i].Email);
  Console.WriteLine();
}

// output
// User number: 0
// Name: Julie Lerman
// Email: [email protected]
// User number: 1
// Name: Mark Lettuce
// Email: [email protected]
// User number: 2
// Name: Peter Mbanugo
// Email: [email protected]

As you can see from the example above, using the indexer is similar to how you've already been using indexers in .NET.

Indexing Using String

We used integers for indexing in the previous example, but you can also use any other type as argument for the indexer. Let's update the implementation of UserCollection to use a string argument type for the indexer method.

class UserCollection
{
  Dictionary<string, User> users = new Dictionary<string, User>();

  public User this[string name]
  {
    get => (User) users[name];
    set => users[name] = value;
  }
}

// using the indexer
static void Main(string[] args)
{
  var users = new UserCollection();

  // add objects using indexer
  users["julie"] = new User("Julie Lerman", "[email protected]");
  users["mark"] = new User("Mark Lettuce", "[email protected]");
  users["peter"] = new User("Peter Mbanugo", "[email protected]");

  // obtain and display Mark's data
  Console.WriteLine($"Marks Email: {users["mark"].Email}");
  Console.Read();
}

The implementation now uses a dictionary to hold the list of users because it allows us to store data with keys of any type. With that, you can add and retrieve user objects with a string value when using the indexer. When you run the code, it should output Mark's Email: [email protected] to the console.

Overloading Indexers

Indexer methods can also be overloaded. If you find yourself in a situation where you'd like to access items using a numerical value or string value, you can define multiple indexer methods for that type, thereby having overloaded indexers. Following on with our example from the previous section, let's update the UserCollection to include a numerical indexer and another that accepts string value type:

class UserCollection
{
  Dictionary<string, User> users = new Dictionary<string, User>();

  public User this[string name]
  {
    get => (User) users[name];
    set => users[name] = value;
  }

  public User this[int key]
  {
    get => (User) users[key.ToString()];
    set => users[key.ToString()] = value;
  }

  public int Count => users.Count;
}

The indexer methods for the UserCollection has two overloads: one that takes integer and another that takes a string value. You can have as many overloads as you'd like, just like you would for methods you define in your classes. The code below shows an example usage of both indexers defined in UserCollection .

var users = new UserCollection();

// add objects using indexer
users["julie"] = new User("Julie Lerman", "[email protected]");
users["mark"] = new User("Mark Lettuce", "[email protected]");
users[3] = new User("Peter Mbanugo", "[email protected]");
users[3] = new User("Peter Liko", "[email protected]");

Console.WriteLine($"{users[3].Name} - {users[3].Email}");
Console.Read();

// output
// Peter Liko - [email protected]

You should notice from the code above that you can use either string or integer as parameter types to the indexer methods. It showed two assignments using the integer value, 3 . The second assignment replaces the initial assignment, and calling the get accessor will return the last value set for users[3] , which should be Peter Liko - [email protected] .

That's a Wrap!

Indexers are similar to properties, but are accessed via an index argument rather than a property name. You define them similarly to how you would define properties but using this[] syntax. I showed you how to declare it with examples of how to have overloaded indexer methods and how to use them.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK