JD2022-TU1/main/tools/framework/JD.Collections/ObservableKeyedList.cs

210 lines
5.5 KiB
C#

using System.Collections.Generic;
using JD.Collections.Interfaces;
using JD.Kernel.StaticHelpers;
namespace JD.Collections
{
/// <summary>
/// A Keyed collection that you can listen to through the CollectionChanged event.
/// </summary>
public abstract class ObservableKeyedList<TKey, TValue> : ObservableList<TValue>, IObservableKeyedList<TKey, TValue>
{
#region Variables
private readonly KeyedCollection m_dictionary;
#endregion
#region Constructor
/// <summary>
/// Ctor
/// </summary>
protected ObservableKeyedList()
{
m_dictionary = new KeyedCollection(this);
}
/// <summary>
/// Override default key Comparer
/// </summary>
/// <param name="comparer"></param>
protected ObservableKeyedList(IEqualityComparer<TKey> comparer)
{
m_dictionary = new KeyedCollection(this, comparer);
}
#endregion
#region Methods
/// <summary>
/// Get value from key.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
/// <remarks>Does not throw when the key is not contained.</remarks>
public virtual TValue this[TKey key]
{
get
{
if (Equals(key, null))
{
return default(TValue);
}
TValue value;
m_dictionary.TryGetValue(key, out value);
return value;
}
}
/// <summary>
/// Removes the item with the given <paramref name="key"/>.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Remove(TKey key)
{
TValue value;
if (!m_dictionary.TryGetValue(key, out value))
{
return false;
}
return Remove(value);
}
/// <summary>
/// When implemented in a derived class, extracts the key from the specified element.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected abstract TKey GetKeyForItem(TValue value);
/// <summary>
/// Whether the keyed collection contains the key.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public virtual bool ContainsKey(TKey key)
{
if (Equals(key, null))
{
return false;
}
return m_dictionary.ContainsKey(key);
}
/// <summary>
/// True if it contains the item.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public override bool Contains(TValue item)
{
return !Equals(item, null) && ContainsKey(GetKeyForItem(item));
}
/// <summary>
/// Overridden.
/// </summary>
/// <param name="index"></param>
/// <param name="item"></param>
protected override void InsertItem(int index, TValue item)
{
Throw.IfNull(item, "Cannot add a null item");
m_dictionary.Add(item);
base.InsertItem(index, item);
}
/// <summary>
/// Overridden.
/// </summary>
/// <param name="index"></param>
protected override void RemoveItem(int index)
{
m_dictionary.Remove(GetKeyForItem(base[index]));
base.RemoveItem(index);
}
/// <summary>
/// Overridden.
/// </summary>
/// <param name="index"></param>
/// <param name="item"></param>
protected override void SetItem(int index, TValue item)
{
Throw.IfNull(item, "Cannot add a null item");
TValue oldItem = base[index];
if (!Equals(oldItem, item))
{
m_dictionary.Remove(oldItem);
m_dictionary.Add(item);
base.SetItem(index, item);
}
}
/// <summary>
/// Overridden.
/// </summary>
protected override void ClearItems()
{
if (Count == 0)
{
return;
}
m_dictionary.Clear();
base.ClearItems();
}
/// <summary>
/// TryGetValue.
/// </summary>
public bool TryGetValue(TKey key, out TValue result)
{
return m_dictionary.TryGetValue(key, out result);
}
public override int IndexOf(TValue item)
{
// Contains is optimized O(1).
if (!Contains(item))
{
return -1;
}
return base.IndexOf(item);
}
#endregion
#region Nested
private class KeyedCollection : KeyedCollection<TKey, TValue>
{
private readonly ObservableKeyedList<TKey, TValue> m_owner;
public KeyedCollection(ObservableKeyedList<TKey, TValue> owner)
{
m_owner = owner;
}
public KeyedCollection(ObservableKeyedList<TKey, TValue> owner, IEqualityComparer<TKey> comparer)
: base(comparer)
{
m_owner = owner;
}
protected override TKey GetKeyForItem(TValue value)
{
return m_owner.GetKeyForItem(value);
}
}
#endregion
}
}