Click here to Skip to main content
15,893,588 members
Articles / Programming Languages / C#

Indexed Dictionary

Rate me:
Please Sign up or sign in to vote.
3.79/5 (9 votes)
4 Jul 2009CPOL6 min read 85.8K   668   43  
An article on a generic collection accessible both as a dictionary and as an indexed list.
  • indexeddictionary3_src.zip
    • IndexedDictionary
      • _ReSharper.IndexedDictionary
        • CachesImage.bin
        • ProjectModel
          • ProjectModel.dat
        • TodoCache
          • .version
          • 8
            • 7c6b5622.dat
        • WebsiteFileReferences
          • .version
        • WordIndex.New
          • .version
          • 2
            • 17367f48.dat
      • IndexedDictionary.4.5.resharper.user
      • IndexedDictionary.sln
      • IndexedDictionary.vsmdi
      • IndexedDictionary
        • _ReSharper.IndexedDictionary
          • 7fe8c250-bc51-4d48-9320-ee2bc2d48386.Metadata
          • 7fe8c250-bc51-4d48-9320-ee2bc2d48386.XmlDocIndex
          • CachesImage.bin
          • ProjectModel
            • ProjectModel.dat
          • ReflectionCache.xml
          • TodoCache
            • .version
            • 0
              • 2240425d.dat
            • 8
          • WebsiteFileReferences
            • .version
          • WordIndex.New
            • .version
            • 2
            • 9
              • 28916f49.dat
          • Xaml
            • CacheProvider.dat
        • _UpgradeReport_Files
        • bin
          • Debug
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
        • IndexedDictionary.cs
        • IndexedDictionary.csproj
        • IndexedDictionary.suo
        • Properties
        • UpgradeLog.XML
      • IndexedDictionaryTest
      • LocalTestRun.testrunconfig
      • TestResults
        • Asher_ZIV-22 2009-06-30 08_38_01.trx
        • Asher_ZIV-22 2009-06-30 08_38_01
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_38_26.trx
        • Asher_ZIV-22 2009-06-30 08_38_26
          • In
            • ZIV-22
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_39_16.trx
        • Asher_ZIV-22 2009-06-30 08_39_16
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_39_31.trx
        • Asher_ZIV-22 2009-06-30 08_39_31
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_40_10.trx
        • Asher_ZIV-22 2009-06-30 08_40_10
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_45_19.trx
        • Asher_ZIV-22 2009-06-30 08_45_19
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_45_49.trx
        • Asher_ZIV-22 2009-06-30 08_45_49
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_48_18.trx
        • Asher_ZIV-22 2009-06-30 08_48_18
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_52_55.trx
        • Asher_ZIV-22 2009-06-30 08_52_55
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
        • Asher_ZIV-22 2009-06-30 08_53_13.trx
        • Asher_ZIV-22 2009-06-30 08_53_13
          • Out
            • IndexedDictionary.dll
            • IndexedDictionary.pdb
            • indexeddictionarytest.dll
            • IndexedDictionaryTest.pdb
  • IndexedDictionary2_src.zip
  • IndexedDictionary_src.zip
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Ziv systems, Israel
Israel Israel
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.

Comments and Discussions