KeyedCollection
工場出荷時の設定に伴う余分なオーバーヘッドがそれだけの価値がない場合は、独自のオーバーヘッドを簡単に実装できます。元のKeyedCollection
inSystem.Collections.ObjectModel
は内部的にaDictionary<TKey, TItem>
とaです。これは、との両方でList<TItem>
操作を定義できることを意味します。たとえば、挿入、インデックスによるアクセス、挿入された順序でのコレクションのトラバース(すべてが容易になります)と同時に、キーに基づいたクイックルックアップ(辞書の助けを借りて)を行うことができます。つまり、アイテムを追加または削除するときは、余分なものを保持するための小さなメモリオーバーヘッドを除いて、基礎となる両方のコレクションで実行する必要があります(ただし、オブジェクトはそのように複製されません)。加算速度はあまり影響を受けませんが(加算はO(1))、IList<>
IDictionary<>
IList<>
List<>
List<>
除去速度は少し影響を受けます。
挿入順序やインデックスによるアクセスを気にしない場合:
public class KeyedCollection<TKey, TItem> : ICollection<TItem>
{
MemberInfo _keyInfo;
Func<TItem, TKey> _keySelector;
Dictionary<TKey, TItem> _dict;
public TItem this[TKey key]
{
get { return _dict[key]; }
}
public int Count
{
get { return _dict.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public ICollection<TKey> Keys
{
get { return _dict.Keys; }
}
private ICollection<TItem> Items
{
get { return _dict.Values; }
}
public KeyedCollection(Expression<Func<TItem, TKey>> keySelector, IEqualityComparer<TKey> comparer = null)
{
var keyExpression = keySelector.Body as MemberExpression;
if (keyExpression != null)
_keyInfo = keyExpression.Member;
_keySelector = keySelector.Compile();
_dict = new Dictionary<TKey, TItem>(comparer);
}
private TKey GetKeyForItem(TItem item)
{
return _keySelector(item);
}
public bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
public bool Contains(TItem item)
{
return ContainsKey(GetKeyForItem(item));
}
public bool TryGetItem(TKey key, out TItem item)
{
return _dict.TryGetValue(key, out item);
}
public void Add(TItem item)
{
_dict.Add(GetKeyForItem(item), item);
}
public void AddOrUpdate(TItem item)
{
_dict[GetKeyForItem(item)] = item;
}
public bool UpdateKey(TKey oldKey, TKey newKey)
{
TItem oldItem;
if (_keyInfo == null || !TryGetItem(oldKey, out oldItem) || !SetItem(oldItem, newKey)) // important
return false;
RemoveKey(oldKey);
Add(oldItem);
return true;
}
private bool SetItem(TItem item, TKey key)
{
var propertyInfo = _keyInfo as PropertyInfo;
if (propertyInfo != null)
{
if (!propertyInfo.CanWrite)
return false;
propertyInfo.SetValue(item, key, null);
return true;
}
var fieldInfo = _keyInfo as FieldInfo;
if (fieldInfo != null)
{
if (fieldInfo.IsInitOnly)
return false;
fieldInfo.SetValue(item, key);
return true;
}
return false;
}
public bool RemoveKey(TKey key)
{
return _dict.Remove(key);
}
public bool Remove(TItem item)
{
return RemoveKey(GetKeyForItem(item));
}
public void Clear()
{
_dict.Clear();
}
public void CopyTo(TItem[] array, int arrayIndex)
{
Items.CopyTo(array, arrayIndex);
}
public IEnumerator<TItem> GetEnumerator()
{
return Items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
ICollection<TItem>
私はそれをより標準に準拠するように実装しました-そしてまたあなたは素晴らしいコレクション初期化構文を手に入れます!:)
使用例:
var p1 = new Person { Name = "a" };
var p2 = new Person { Name = "b" };
var people = new KeyedCollection<string, Person>(p => p.Name) { p1, p2 };
// p1 == people["a"];
// p2 == people["b"];