Search code examples
c#dictionaryiterationvisitor-pattern

Iterate trough an heterogeneous and type-safe dictionary


I need a container that works like a ditionary but where the type of data (TValue) change from one key to the other.

I also need to iterate trough it.


Solution

  • For the heterogeneous and type-safe dictionary part

    Wilka response is a good start.

    The trick is to put the type in the key.

    /// <summary>
    /// Base class for all dictionary key.
    /// 
    /// <remarks>The key name is REALLY usefull for debug purpose.</remarks>
    /// </summary>
    abstract class HeterogeneousDictionaryKeyBase
    {
        readonly string _name;
    
        protected HeterogeneousDictionaryKeyBase(string name)
        {
            _name = name;
        }
    
        public override string ToString()
        {
            return _name;
        }
    }
    
    sealed class HeterogeneousDictionaryKey<TValue> : HeterogeneousDictionaryKeyBase
    {
        public HeterogeneousDictionaryKey(string name)
            : base(name)
        {
        }
    }
    

    So calls to dictionary will have a generic value type:

    /// <summary>
    /// <remarks>The [] operator can not be generic, so we implement it has a getter and a setter</remarks>
    /// </summary>
    class HeterogeneousDictionary
    {
        private readonly Dictionary<HeterogeneousDictionaryKeyBase, object> _dictionary = new Dictionary<HeterogeneousDictionaryKeyBase, object>();
    
        public void Add<TValue>(HeterogeneousDictionaryKey<TValue> key, TValue value)
        {
            _dictionary.Add(key, value);
        }
    
        public TValue Get<TValue>(HeterogeneousDictionaryKey<TValue> key)
        {
            return (TValue)_dictionary[key];
        }
    
        public void Set<TValue>(HeterogeneousDictionaryKey<TValue> key, TValue value)
        {
            _dictionary[key] = value;
        }
    
        public bool TryGetValue<TValue>(HeterogeneousDictionaryKey<TValue> key, out TValue value)
        {
            object result;
            if (_dictionary.TryGetValue(key, out result) && result is TValue)
            {
                value = (TValue)result;
                return true;
            }
    
            value = default(TValue);
            return false;
        }
    }
    

    The usage is simple:

    var dictionary = new HeterogeneousDictionary();
    
    var keyName = new HeterogeneousDictionaryKey<string>("keyName");
    var keyAge = new HeterogeneousDictionaryKey<int>("keyAge");
    
    dictionary.Set(keyName, "Orace");
    dictionary.Set(keyAge, 8);
    
    ...
    
    var name = dictionary.Get(keyName);
    var age = dictionary.Get(keyAge);
    

    For the iteration part

    A visitor pattern against the dictionary keys will do the trick.

    First the visitor interface:

    interface IHeterogeneousDictionaryKeyVisitor
    {
        void Visit<TValue>(HeterogeneousDictionaryKey<TValue> key);
    }
    

    Then we made the HeterogeneousDictionaryKey cooperate:

    abstract class HeterogeneousDictionaryKeyBase
    {
        ...
    
        public abstract void Accept(IHeterogeneousDictionaryKeyVisitor visitor);
    
        ...
    }
    
    sealed class HeterogeneousDictionaryKey<TValue> : HeterogeneousDictionaryKeyBase
    {
        ...
    
        public override void Accept(IHeterogeneousDictionaryKeyVisitor visitor)
        {
            visitor.Visit(this);
        }
    }
    

    Now we can expose the HeterogeneousDictionary keys:

    class HeterogeneousDictionary
    {
        ...
    
        public Dictionary<HeterogeneousDictionaryKeyBase, object>.KeyCollection Keys
        {
            get { return _dictionary.Keys; }
        }
    
        ...
    }
    

    And that it all.

    Here an example of usage to safely copy a dictionary to an other

    class DictionaryCopier : IHeterogeneousDictionaryKeyVisitor
    {
        readonly HeterogeneousDictionary _source;
        readonly HeterogeneousDictionary _destination;
    
        public DictionaryCopier(HeterogeneousDictionary source, HeterogeneousDictionary destination)
        {
            _source = source;
            _destination = destination;
        }
    
        public void PerformCopy()
        {
            foreach (var key in _source.Keys)
            {
                // See you soon.
                key.Accept(this);
            }
        }
    
        /// <summary>
        /// We fall back here with a typed key.
        /// </summary>
        public void Visit<TValue>(HeterogeneousDictionaryKey<TValue> key)
        {
            // Here the value is typed.
            var value = _source.Get(key);
    
            _destination.Add(key, value);
        }
    }