views:

104

answers:

3

istead of using Dictionary I want some type of collection class that can use a property of the value as the key, is there something like this?

+2  A: 

Use a normal one, and when you set the key value pair, specify the property of the value you are interested in.

That was too easy, I must be misunderstanding your request.

Maybe you wanted to use an arbitrary property later rather than at input time. In that case, I think you would have to use multiple dictionary objects (perhaps tied together in a helper class).

Karl
If you set the property of the object after you add it, it will be out of sync with the key in the collection.
Jeremy
+7  A: 

Yes, there is - System.Collections.ObjectModel.KeyedCollection<TKey, TValue>.

That's abstract, and there are no concrete derived classes in the framework as far as I can see, but all you need to implement is GetKeyForItem as far as I can see. For example, you could do this with a delegate:

public class DelegatingKeyedCollection<TKey, TItem> : System.Collections.ObjectModel.KeyedCollection<TKey, TItem>
{
    private readonly Func<TItem, TKey> keySelector;

    public DelegatingKeyedCollection(Func<TItem, TKey> keySelector)
    {
        this.keySelector = keySelector;
    }

    protected override TKey GetKeyForItem(TItem item)
    {
        return keySelector(item);
    }
}
Jon Skeet
+2  A: 

KeyedCollection as Jon Skeet says is the obvious candidate.

A few random remarks about this class:

  • You will of course want the property that you use as the key to be readonly.

  • Its method Contains(TItem item) is inherited from Collection<T>, and is implemented by iterating through the collection. This can therefore be much slower than Contains(TKey key). It's too easy for developers to make the mistake of using the wrong overload, so it may be worth considering implementing your own Contains(TItem item) method:

    public new bool Contains(TItem item)
    {
        if (item == null) throw new ArgumentNullException("item");
        return this.Contains(GetKeyForItem(item));
    }
    
  • Unlike an IDictionary, it doesn't have a method TryGetValue. This can be useful and it might be worth implementing your own:

    public bool TryGetValue(TKey key, out TItem item)
    {
        // If the dictionary exists, use it
        if (Dictionary != null) return Dictionary.TryGetValue(key, out item);
        // Else do it the hard way
        if (!this.Contains(key))
        {
            item = default(TItem);
            return false;
        }
        item = this[key];
        return true;
    }
    
  • It doesn't support enumeration of the keys, which can be useful:

    public IEnumerable<TKey> GetKeys()
    {
        foreach (TItem item in this)
        {
            yield return GetKeyForItem(item);
        }
    }
    
  • Serialization can be inefficient, as it will serialize both its internal list and its internal dictionary. You can get round this if you need to by implementing custom serialization.

Joe