Search code examples
c#genericsdictionaryoverridingencapsulation

Encapsulate Dictionary using IDictionary Interface instead of Overriding or Hiding Method


I couldn't find a complete example of how to properly encapsulate a Dictionary. All I needed/wanted to do was 'override' the Add method which I know can't be overridden because it is not virtual. Based on some research I found I needed to encapsulate it using a private Dictionary and implementing the IDictionary Interface.

The most complete example I could find was here, which I mostly copy-pasted and guessed about the things that didn't match (the example implements a read-only dictionary, whereas I want full default functionality with a customized Add() method).

Full Compilable Code below:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace IDictionary
{
    class Program
    {
        static void Main(string[] args)
        {
            Registers<string, dynamic> DynRegistry = new Registers<string, dynamic>();

            DynRegistry.Add("Foo", 100);
            Console.WriteLine("DynRegistry Size: {0}", DynRegistry.Count);
            foreach (var item in DynRegistry)
            {
                Console.WriteLine("\tName: {0}, Value: {1}", item.Key, item.Value);
            }
            DynRegistry.Add("Foo2", "Hello World");
            Console.WriteLine("DynRegistry Size: {0}", DynRegistry.Count);
            foreach (var item in DynRegistry)
            {
                Console.WriteLine("\tName: {0}, Value: {1}", item.Key, item.Value);
            }
            DynRegistry.Add("Foo", true);
            Console.WriteLine("DynRegistry Size: {0}\r\n", DynRegistry.Count);
            foreach (var item in DynRegistry)
            {
                Console.WriteLine("\tName: {0}, Value: {1}", item.Key, item.Value);
            }
            Console.ReadKey();
        }
    }

    class Registers<TKey, TValue> : IDictionary<TKey, TValue>,  ICollection
    {
        //Fields
        private Dictionary<TKey, TValue> source;
        private object syncRoot;

        //Constructor
                    public Registers()
    {
        this.source = new Dictionary<TKey, TValue>();
    }

        //Wrapped Methods
                                                public void Add(TKey key, TValue value)
    {
        if (this.source.ContainsKey(key))
        {
            this.source[key] = value;
        }
        else
        {
            this.source.Add(key, value);
        }
    }

        //Implement default Interfaces
                        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        ICollection<KeyValuePair<TKey, TValue>> collection = this.source;
        collection.CopyTo(array, arrayIndex);
    }
        public int Count { get { return this.source.Count; } }
        public ICollection<TKey> Keys { get { return this.source.Keys; } }
        public ICollection<TValue> Values { get { return this.source.Values; } }
        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { get { return false; } }
        bool ICollection.IsSynchronized { get { return false; } }
        object ICollection.SyncRoot
    {
        get
        {
            if (this.syncRoot == null)
            {
                ICollection collection = this.source as ICollection;

                if (collection != null)
                {
                    this.syncRoot = collection.SyncRoot;
                }
                else
                {
                    Interlocked.CompareExchange(ref this.syncRoot, new object(), null);
                }
            }
            return this.syncRoot;
        }
    }
        public TValue this[TKey key]
    {
        get { return this.source[key]; }
        set { this.source[key] = value; }
    }
        public bool ContainsKey(TKey key) { return this.source.ContainsKey(key); }
        public bool Remove(TKey key) { return this.source.Remove(key); }
        public bool TryGetValue(TKey key, out TValue value) { return this.source.TryGetValue(key, out value); }
        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
    {
        ICollection<KeyValuePair<TKey, TValue>> collection = this.source;
        collection.Add(item);
    }
        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
    {
        ICollection<KeyValuePair<TKey, TValue>> collection = this.source;
        collection.Clear();
    }
        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
    {
        ICollection<KeyValuePair<TKey, TValue>> collection = this.source;
        return collection.Contains(item);
    }
        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
    {
        ICollection<KeyValuePair<TKey, TValue>> collection = this.source;
        return collection.Remove(item);
    }
        void ICollection.CopyTo(Array array, int index)
    {
        ICollection collection = new List<KeyValuePair<TKey, TValue>>(this.source);
        collection.CopyTo(array, index);
    }
        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
    {
        IEnumerable<KeyValuePair<TKey, TValue>> enumerator = this.source;
        return enumerator.GetEnumerator();
    }
        IEnumerator IEnumerable.GetEnumerator()
    {
        return this.source.GetEnumerator();
    }
    }
}

Two reasons I am posting this:

  1. So that anyone else that needs to do this doesn't have to spend 30 minutes re-typing redundant code.
  2. Feedback about whether I did this all correctly. I was confused as to if/why I needed to implement the ICollection interface (ie. Does a normal Dictionary do this or would the IDictionary have the complete complement of Dictionary features without the need for ICollection).

All I really needed was a normal Dictionary with an Add method that does AddOrReplace() instead of Add (or crash because the Key already exists). The work required to achieve this seemed to be a bit of overkill.


Solution

  • You can use

    dict[key]=value
    

    for add or replace if that is all you want. So no need to wrap a dictionary.