使用隐式DictionaryEntry迭代IDictionary

时间:2013-10-21 08:38:13

标签: c# idictionary

考虑以下代码:

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

工作正常。由于variablesIDictionary,因此它由DictionaryEntryobject Keyobject Value组成。

为什么我不能输入foreach(var vari in variables)?它给了我

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

这似乎很奇怪,我找不到这种行为的原因。 DictionaryEntrystruct,但我可以对List<DictionaryEntry>进行迭代。我当然理解IDictionary不是通用的,但manual表示它包含DictionaryEntries,因此应该可以使用var ...

3 个答案:

答案 0 :(得分:13)

  

为什么我不能输入foreach(变量中的var vari)?

你可以 - 但是vari隐含的是object类型。

您碰巧知道迭代器中的每个条目都是DictionaryEntry,但编译器却没有。据他所知,IDictionary的迭代元素类型只是object。即使IDictionary.GetEnumerator返回IDictionaryEnumerator,但仍有Current属性,其类型为object,而不是DictionaryEntry

令人讨厌的是,这个可以做得更好。如果使用IDictionaryEnumerator的显式接口实现实现了IEnumerator.Current,并提供了类型为Current的新DictionaryEntry属性,则可以使用效率更高,因为它可以避免拳击。

C#规范的第8.8.4节提供了C#编译器用于确定集合的元素类型的规则。

编辑:对于那些想要了解IDictionaryEnumerator 如何宣布的人,这里有一个简短而完整的例子。请注意,这不会在任何地方使用泛型,但 var中使用Main,仍然隐式输入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);
        }
    }
}

答案 1 :(得分:2)

如果您明确未提供vari的类型,则会将其视为object,因为variablesIEnumerable,而不是{{1} }}

IEnumerable<DictionaryEntry>

答案 2 :(得分:2)

foreach循环包含显式强制转换。所以你会在foreach循环中得到一些东西:

vari = (DictionaryEntry)e.Current;

如果object您将被转换为对象,从而调用KeyValue非编译。

来自 C#规范 8.8.4 foreach声明

foreach (V v in x) 
  embedded-statement

被翻译成:

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

考虑Eric关于此主题的文章Why does a foreach loop silently insert an "explicit" conversion?