Search code examples
c#idictionary

Iterate over IDictionary with implicit DictionaryEntry


Consider this code:

var variables = System.Environment.GetEnvironmentVariables();
foreach (DictionaryEntry vari in variables)
{
    Console.WriteLine(vari.Key);
    Console.WriteLine(vari.Value);
}

It works fine. Since variables is IDictionary, it consists of DictionaryEntry, with object Key and object Value.

Why cannot I type foreach(var vari in variables)? It gives me

error CS1061: 'object' does not contain a definition for 'Key/Value'...

It seems strange and I cannot find a reason for this behaviour. DictionaryEntry is a struct, but I can iterate over a List<DictionaryEntry> all right. Of course I understand that IDictionary is not generic, but the manual says it contains DictionaryEntries, so it should be possible to use var...


Solution

  • Why cannot I type foreach(var vari in variables)?

    Well you can - but then vari is implicitly of type object.

    You happen to know that each entry within the iterator is a DictionaryEntry, but the compiler doesn't. As far as it's aware, the iteration element type of IDictionary it just object. Even though IDictionary.GetEnumerator returns an IDictionaryEnumerator, that still has a Current property with a type of object, not DictionaryEntry.

    Annoyingly, this could have been done better. If IDictionaryEnumerator had been implemented using explicit interface implementation for IEnumerator.Current, and provided a new Current property of type DictionaryEntry, then this would have worked and been more efficient as it would have avoided boxing.

    Section 8.8.4 of the C# spec provides the rules the C# compiler uses to determine the element type of a collection.

    EDIT: For those who wanted to see how IDictionaryEnumerator could have been declared, here's a short but complete example. Note how this doesn't use generics anywhere, but does use var in Main, still with a variable implicitly typed as DictionaryEntry:

    using System;
    using System.Collections;
    
    interface IJonDictionary : IEnumerable
    {
        new IJonDictionaryEnumerator GetEnumerator();
    }
    
    interface IJonDictionaryEnumerator : IEnumerator
    {
        new DictionaryEntry Current { get; }
    }
    
    class JonDictionary : IJonDictionary
    {
        private readonly IDictionary dictionary = new Hashtable();
    
        public object this[object key]
        {
            get { return dictionary[key]; } 
            set { dictionary[key] = value; }
        }
    
        public void Add(object key, object value)
        {
            dictionary.Add(key, value);
        }
    
        public IJonDictionaryEnumerator GetEnumerator()
        {
            return new JonEnumerator(dictionary.GetEnumerator());
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        private class JonEnumerator : IJonDictionaryEnumerator
        {
            private readonly IDictionaryEnumerator enumerator;
    
            internal JonEnumerator(IDictionaryEnumerator enumerator)
            {
                this.enumerator = enumerator;
            }
    
            public DictionaryEntry Current
            {
                get { return enumerator.Entry; }
            }
    
            object IEnumerator.Current { get { return Current; } }
    
            public bool MoveNext()
            {
                return enumerator.MoveNext();
            }
    
            public void Reset()
            {
                enumerator.Reset();
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var dictionary = new JonDictionary { 
                { "x", "foo" },
                { "y", "bar" }
            };
    
            foreach (var entry in dictionary)
            {
                Console.WriteLine("{0} = {1}", entry.Key, entry.Value);
            }
        }
    }