One feature that Dictionary<> doesn’t support is the ability to access items by integer index. That is, insertion order is not maintained. For most cases, this may be ok, but some cases require fast search and index based access.

Let’s suppose that the Id of a Customer is unique, and so can serve as a key. Here’s a dictionary of such customers:

var customers = newDictionary<int, Customer>();

customers.Add(1, newCustomer { Id = 1, Name = "Bart Simpson" });

customers.Add(20, newCustomer { Id = 20, Name = "Homer Simpson" });

This seems harmless enough, but notice that the key (Id) is supplied twice. This may not seem like a big deal, but the key could be a more complex entity. It’s also not as elegant as we would like. I’d rather just use code like the following:

customers.Add(newCustomer { Id = 1, Name = "Bart Simpson" });

And get the same result, that is, the Id is still a key to search by. Dictionary<> cannot take that syntax. Enter KeyedCollection<>.

KeyedCollection<TKey, TValue> is an abstract base class that inherits from Collection<TValue>, thus giving it index-based access. A requirement is that the key is somewhere inside the value, or connected to the value, as is the case with our Customer class. The only question remains is how to return a key based on a value?

This is why we need a new class, extending KeyedCollection<> and overriding a single abstract method, GetKeyForItem:

classCustomerCollection : KeyedCollection<int, Customer> {

protectedoverrideint GetKeyForItem(Customer item) {

return item.Id;

}

}

This says that given a Customer, its key is its Id. Now we can use this class as follows:

var customers = newCustomerCollection();

customers.Add(newCustomer { Id = 1, Name = "Bart Simpson" });

customers.Add(newCustomer { Id = 20, Name = "Homer Simpson" });

We just add Customer objects, and internally they are keyed by Id. The Add method used is the one from Collection<TValue>. This means, we can also access customers by index, or search using the Contains method.

As an extra bonus, the KeyedCollection<> can be configured to not create an internal Dictionary<>, depending on the number of items. For a small number of items, Dictionary<> is an overkill, and a linear search is faster. By default, a Dictionary is created upon first insertion, but this can be configured by using one of KeyedCollection<>’s constructors.

The idea of mapping a value to a key as was done in GetKeyForItem can be generalized, so that we don’t have to create new classes for new types. Here’s a generalized class that uses a delegate to execute the required code in GetKeyForItem: