Существует ли общая конкретная реализация KeyedCollection?

Класс System.Collections.ObjectModel.KeyedCollection является очень полезной альтернативой System.Collections.Generic.Dictionary, особенно когда ключевые данные являются частью объекта, который хранится, или вы хотите иметь возможность перечислять элементы в порядке. К сожалению, класс является абстрактным, и я не могу найти общую конкретную реализацию в основной платформе .NET.

Руководство по дизайну каркаса указывает, что конкретная реализация должна быть предоставлена ​​для абстрактных типов (раздел 4.4. Дизайн абстрактного класса). Почему разработчики фреймворка не учитывают общую конкретную реализацию такого полезного класса, особенно когда это может быть обеспечено просто разоблачением конструктора, который принимает и сохраняет Converter от элемента к его ключу:

public class ConcreteKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>
{
    private Converter<TItem, TKey> getKeyForItem = null;
    public ConcreteKeyedCollection(Converter<TItem, TKey> getKeyForItem)
    {
        if (getKeyForItem == null) { throw new ArgumentNullException("getKeyForItem"); }
        this.getKeyForItem = getKeyForItem;
    }
    protected override TKey GetKeyForItem(TItem item)
    {
        return this.getKeyForItem(item);
    }
}

Ответы

Ответ 1

Существуют конкретные реализации, включая (но не ограничиваясь ими):

В духе вашего вопроса нет никакой общей реализации, и поскольку я не работаю в Microsoft, я могу только догадываться. Поскольку конкретная реализация так же проста, как вы показали, я не буду предлагать какие-либо предположения (как это, вероятно, было бы неправильно).

Ответ 2

Вот реализация, с которой я придумал

public class LookupKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>
{
    private Func<TItem, TKey> _getKeyFunc;

    public LookupKeyedCollection(Func<TItem, TKey> getKeyFunc)
    {
        _getKeyFunc = getKeyFunc;
    }

    //Required KeyedCollection implementation
    protected override TKey GetKeyForItem(TItem item)
    {
        return _getKeyFunc(item);
    }

    public bool TryGetItem(TKey key, out TItem item)
    {
        if (Dictionary == null)
        {
            item = default(TItem);
            return false;
        }

        return Dictionary.TryGetValue(key, out item);
    }

    public void AddOrUpdate(TItem item)
    {
    Remove(_getKeyFunc(item));
    Add(item);
    }

    public new bool Contains(TItem item)
    {
        return base.Contains(_getKeyFunc(item));
    }
}

Обоснование методов в основном можно найти в следующем:

Ответ 3

Причина, по которой нет конкретной реализации, заключается в том, что она не будет сериализована (вы не можете сериализовать делегат). Все коллекции в BCL являются сериализуемыми.

Для этого лучше наследовать и переопределять метод, особенно если вы не можете предсказать, как будет использоваться коллекция.

Ответ 4

Вот что я придумал. Он либо жестко кодирует имена свойств, либо вы можете использовать атрибут [Key], если хотите.

    ///// <summary>
///// Creates an indexed list.  Requires that [Key] attribute be applied to a property in TValue object.
///// </summary>
///// <example>
///// public class Test
///// {
/////     [Key]
/////     public int Id { get; set; }
///// }
///// 
///// IndexedList<int, Test> tests;
///// </example>
///// <typeparam name="TKey"></typeparam>
///// <typeparam name="TValue"></typeparam>
public class IndexedList<TKey, TValue> : KeyedCollection<TKey, TValue>
{
    PropertyInfo keyProperty;

    public IndexedList()
    {
        foreach (var property in typeof(TValue).GetProperties())
        {
            // this requires .net 4, which I couldn't use due to the WPF shadow effect deprication
            //if (property.PropertyType == typeof(TKey) && property.IsDefined(typeof(KeyAttribute), true))

            if (property.PropertyType == typeof(TKey) && (property.Name.ToUpper() == "ID" || property.Name.ToUpper() == "KEY"))
            {
                keyProperty = property;
                return;
            }
        }

        throw new ArgumentException(String.Format("Unable to find a property in {0} that is named Id or Key and is of type {1}.", typeof(TValue).Name, typeof(TKey).Name));
    }

    protected override TKey GetKeyForItem(TValue item)
    {
        return (TKey)keyProperty.GetValue(item, null);
    }
}