65.9K
CodeProject is changing. Read more.
Home

KeyedList, Extending the KeyedCollection

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.11/5 (5 votes)

Sep 26, 2008

CDDL

2 min read

viewsIcon

41692

downloadIcon

178

An attempt to offer an alternative to the generic KeyedCollection.

Introduction

This an attempt to offer an alternative to the generic KeyedCollection for ease of use.

Background

I'm a fan of the KeyedCollection. Simply because, in a simple way, it resembles how a table in a database works. The objects in the KeyedCollection contain their key values. This makes it a good candidate for caching and handling business data in memory in a database-like manner.

However, a few thing are annoying when dealing with the KeyedCollection:

  • It's abstract, so you'll have to make an implementation to use it.
  • Unlike tables from a database, you can use only one value as your key.

The primary objective

The primary objective of the KeyedList project is to make a more easy-to-use version of the Generic KeyedCollection, that can be used like this:

KeyedList<string, MyClass> list = new KeyedList<string, MyClass>();
if(list.Contains("key"))
{
    MyClass item = list["key"];
}

Using the code

The KeyedCollection is abstract, and requires an implementation to work; more specifically, you'll have to implement a method GetKeyForItem() that will provide a key for the KeyedCollection for the items in the list.

The KeyedList is, in fact, an implementation of the KeyedCollection, but it offers three alternate ways to let the user provide keys for the chosen item.

  1. By passing in a delegate on instantiation:
  2. KeyedList<string, MyClass> list = 
       new KeyedList<string, MyClass>(x => x.KeyValue);
    
    // or if you don't like the lampda
    KeyedList<string, MyClass> list = 
       new KeyedList<string, MyClass>( delegate(MyClass x){ return x.Keyvalue; } );
  3. Implementing an interface on the target class:
  4. class MyClass : IKeyedItem<string>  
    {
        string IKeyedItem<string>.Key     
        {        
            get { return this.KeyValue; }  
                }  
    }
    
    KeyedList<string, MyClass> list = new KeyedList<string, MyClass>(); 
    // etc..
  5. Or finally, by decorating the "Key" property with a DataObjectField attribute:
  6. class MyClass : IKeyedItem<string>  
    {      
        [DataObjectField(true)]      
        public string KeyValue { get; set; }  
    }  
    
    KeyedList<string, MyClass> list = new KeyedList<string, MyClass>();  
    // etc..

When the key is a combination of values

Then what? Today's Dictionarys and KeyedCollections do not offer a straightforward solution. Let's imagine we have a table that holds order lines... the key would usually be a combination of an OrderId and a LineNumber. The key is only unique when you combine these values. How do you represent that structure using a Dictionary to enforce the uniqueness?

// I was goingfor something like this:

KeyedList<int, string, MyClass> list = new KeyedList<int, string, MyClass>();
if(list.Contains(2, "key"))
{
    MyClass item = list[2,"key"];
}
//  etc ...

I have a suggestion on how to resolve this issue. By using a lightweight generic structure to hold the values as if they were one, and at the same time, providing its own comparer for the Dictionary.

public struct Key<T1, T2> : IEquatable<Key<T1, T2>>
{
      public Key(T1 value1, T2 value2)
    {
               // ...
    }
}

class KeyEqualityComparer<T1, T2> : EqualityComparer<Key<T1, T2>>
{
    // ...
}

You can actually use it directly with a normal Dictionary, like this:

Dictionary<Key<int, int>, MyClass> dictionary = 
  new Dictionary<Key<int, int>, MyClass>(Key<int, int>.Comparer);
// etc..
    
MyClass item = dictionary[new Key(123, 456)];

However, I find it more elegant and easy to use to have it embedded directly in the KeyedList itself.

If you have inputs or feel like adding, correcting, or improve the code, please join the project at Codeplex.

Points of interest

I find that caching more and more data in a more and more complex form seems to be a trend. For that reason, I applaud whenever new and interesting methods and technologies that make this easier comes along... first, the anonymous methods, and later, Lambda expressions and LINQ for Objects. However, I always prefer simple objects over ADO DataSets and DataTables to hold my data in memory; they are simply too heavy and too slow if your data reaches certain amounts. So, out of necessity, I made this implementation.

History

None so far.