Существует ли общая конкретная реализация 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);
}
}