|
using System;
using System.Collections.Generic;
using System.Text;
namespace IndexedDictionary
{
/// <summary>
/// An indexable Dictionary
/// </summary>
/// <typeparam name="TKey">type for the key</typeparam>
/// <typeparam name="TValue">type for the value</typeparam>
public class IndexedDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
protected List<TKey> m_col = new List<TKey>();
protected bool m_bReplaceDuplicateKeys = false; //can key be inserted more than once?
//if so then it will if it contains the key on add
protected bool m_bThrowErrorOnInvalidRemove = false; //if true then throws an exception if the
//item you are trying to remove does not exist in the collection
#region Contructors
public IndexedDictionary()
{
ValidateKeyType();
}
public IndexedDictionary(bool bReplaceDuplicateKeys)
{
ValidateKeyType();
m_bReplaceDuplicateKeys = bReplaceDuplicateKeys;
}
public IndexedDictionary(bool bReplaceDuplicateKeys, bool bThrowErrorOnInvalidRemove)
: this(bReplaceDuplicateKeys)
{
ValidateKeyType();
m_bThrowErrorOnInvalidRemove = bThrowErrorOnInvalidRemove;
}
/// <summary>
/// Makes sure int is not used as dictionary key:
/// </summary>
private static void ValidateKeyType()
{
if (typeof(TKey) == typeof(int))
{
throw new IndexedDictionaryException("Key of type int is not supported.");
}
}
#endregion
public bool ReplaceDuplicateKeys
{
get { return m_bReplaceDuplicateKeys; }
}
public bool ThrowErrorOnInvalidRemove
{
get { return m_bThrowErrorOnInvalidRemove; }
}
//useful for changing the key in subclasses.
protected virtual TKey TransformKey(TKey key)
{
return key;
}
public bool Contains(TKey key)
{
return base.ContainsKey(TransformKey(key));
}
public virtual new void Add(TKey key, TValue item)
{
AddAt(-1, key, item);
}
public virtual void AddAt(int index, TKey key, TValue item)
{
DictionaryBeforeEventArgs<TKey, TValue> e = new DictionaryBeforeEventArgs<TKey, TValue>
(key, item);
// Raise before events:
bool bubble = true;
if (BeforeAdd != null)
{
foreach (DictionaryBeforeDelegate<TKey, TValue> function in BeforeAdd.GetInvocationList())
{
e.Bubble = true;
function.Invoke(this, e);
bubble = bubble && e.Bubble;
}
}
if (!bubble) return;
// Add item:
// Use value returend by event:
if (m_bReplaceDuplicateKeys && ContainsKey(TransformKey(e.Key))) //check if it contains and remove
Remove(TransformKey(e.Key));
base.Add(TransformKey(e.Key), e.Value);
if (index != -1)
m_col.Insert(index, e.Key);
else
m_col.Add(e.Key);
// Raise after events:
if (AfterAdd != null)
{
AfterAdd.Invoke(this, e);
}
}
public virtual void RemoveAt(int index)
{
if (m_bThrowErrorOnInvalidRemove)
if (index < 0 || index >= m_col.Count)
throw new IndexedDictionaryException("Cannot remove invalid Index");
TKey key = m_col[index];
Remove(TransformKey(key));
}
public new void Remove(TKey key)
{
bool bContains = ContainsKey(TransformKey(key));
if (m_bThrowErrorOnInvalidRemove && !bContains)
throw new IndexedDictionaryException("Key does not exist within the Dictionary");
else if (!bContains)
return;
// Raise before events:
DictionaryBeforeEventArgs<TKey, TValue> e = new DictionaryBeforeEventArgs<TKey, TValue>
(key, base[TransformKey(key)]);
// Raise before events:
bool bubble = true;
if (BeforeRemove != null)
{
foreach (DictionaryBeforeDelegate<TKey, TValue> function in BeforeRemove.GetInvocationList())
{
e.Bubble = true;
function.Invoke(this, e);
bubble = bubble && e.Bubble;
}
}
if (!bubble) return;
// Remove item:
// Use value returend by event:
m_col.Remove(e.Key);
base.Remove(TransformKey(e.Key));
// Raise after events:
if (AfterRemove != null)
{
AfterRemove.Invoke(this, e);
}
}
public TKey GetKeyByIndex(int index)
{
return m_col[index];
}
public new bool ContainsKey(TKey key)
{
return base.ContainsKey(TransformKey(key));
}
#region Indexers
public new TValue this[TKey key]
{
get
{
return base[TransformKey(key)];
}
set
{
if (!base.ContainsKey(TransformKey(key)))
{
throw new IndexedDictionaryException("Values cannot be added directly. Use Add() or AddAt() method.");
}
base[TransformKey(key)] = value;
}
}
public TValue this[int index]
{
get
{
return this[m_col[index]];
}
set
{
this[m_col[index]] = value;
}
}
#endregion
public event DictionaryBeforeDelegate<TKey, TValue> BeforeAdd;
public event DictionaryBeforeDelegate<TKey, TValue> BeforeRemove;
public event DictionaryAfterDelegate<TKey, TValue> AfterAdd;
public event DictionaryAfterDelegate<TKey, TValue> AfterRemove;
public event DictionaryBeforeClearDelegate BeforeClear;
public event DictionaryAfterClearDelegate AfterClear;
/// <summary>
/// Clears all values and raises events.
/// </summary>
public new void Clear()
{
DictionaryBeforeClearEventArgs e = new DictionaryBeforeClearEventArgs();
// Raise before events:
bool bubble = true;
if (BeforeClear != null)
{
foreach (DictionaryBeforeClearDelegate function in BeforeClear.GetInvocationList())
{
e.Bubble = true;
function.Invoke(this, e);
bubble = bubble && e.Bubble;
}
}
if (!bubble) return;
// Clear items item:
base.Clear();
m_col.Clear();
// Raise after events:
if (AfterClear != null)
{
AfterClear.Invoke(this, new EventArgs());
}
}
}
public delegate void DictionaryBeforeDelegate<TKey, TValue>(object sender, DictionaryBeforeEventArgs<TKey, TValue> e);
public delegate void DictionaryAfterDelegate<TKey, TValue>(object sender, DictionaryEventArgs<TKey, TValue> e);
public delegate void DictionaryBeforeClearDelegate(object sender, DictionaryBeforeClearEventArgs e);
public delegate void DictionaryAfterClearDelegate(object sender, EventArgs e);
public class DictionaryEventArgs<TKey, TValue> : EventArgs
{
public DictionaryEventArgs(TKey key, TValue value)
{
_value = value;
_key = key;
}
private TValue _value;
public TValue Value
{
get { return _value; }
}
private TKey _key;
public TKey Key
{
get { return _key; }
}
}
public class DictionaryBeforeEventArgs<TKey, TValue> : DictionaryEventArgs<TKey, TValue>
{
public DictionaryBeforeEventArgs(TKey key, TValue value)
: base(key, value)
{
}
private bool _bubble = true;
public bool Bubble
{
get { return _bubble; }
set { _bubble = value; }
}
}
public class DictionaryBeforeClearEventArgs
{
public DictionaryBeforeClearEventArgs()
{
}
private bool _bubble = true;
public bool Bubble
{
get { return _bubble; }
set { _bubble = value; }
}
}
public class IndexedDictionaryException : ApplicationException
{
public IndexedDictionaryException()
{
}
public IndexedDictionaryException(string message)
: base(message)
{
}
public IndexedDictionaryException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Starting with Apple IIe BASICA, and working my way through Pascal, Power Builder, Visual basic (and the light office VBA) C, C++, I am now a full stack developer and development manager. Mostly with MS technologies on the server side and javascript(typescript) frameworks on the client side.