Question

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...

Was it helpful?

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);
        }
    }
}

OTHER TIPS

If you don't explicitly provide the type for vari, it's considered object since variables is IEnumerable, not IEnumerable<DictionaryEntry>

//As you can imagine this won't work:
foreach (DictionaryEntry vari in variables) {
    object v2 = vari;
    Console.WriteLine(v2.Key);
    Console.WriteLine(v2.Value); 
}

//This works!:
foreach (var vari in variables) {
    DictionaryEntry v2 = (DictionaryEntry) vari;
    Console.WriteLine(v2.Key);
    Console.WriteLine(v2.Value); 
}

foreach loop contain explicit cast. So you'll get something along inside foreach loop:

vari = (DictionaryEntry)e.Current;

In case of object you'll get cast to object, thus making calls to Key and Value non-compiling.

From C# Spec 8.8.4 The foreach statement:

foreach (V v in x) 
  embedded-statement

is translated into:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        while (e.MoveNext()) {
            V v = (V)(T)e.Current; //explicit cast
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

consider Eric's article on this topic Why does a foreach loop silently insert an "explicit" conversion?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top