5

考虑这段代码:

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

它工作正常。因为variablesIDictionary,它由DictionaryEntryobject Key和组成object Value组成。

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

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

这似乎很奇怪,我找不到这种行为的原因。DictionaryEntry是 a struct,但我可以遍历 aList<DictionaryEntry>没关系。当然我知道这IDictionary不是通用的,但是手册上说它包含DictionaryEntries,所以应该可以使用var...

4

3 回答 3

13

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

好吧,您可以-但vari隐含地属于 type object

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

令人讨厌的是,这本来可以做得更好。如果IDictionaryEnumerator已使用 的显式接口实现来实现IEnumerator.Current,并提供了Currenttype 的新属性DictionaryEntry,那么这将有效并且更有效,因为它可以避免装箱。

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

编辑:对于那些想看看如何IDictionaryEnumerator 声明的人,这里有一个简短但完整的例子。请注意,这不会在任何地方使用泛型,而是使用in varMain仍然使用隐式类型为 的变量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);
        }
    }
}
于 2013-10-21T08:44:40.323 回答
2

如果您没有明确提供 for 的类型vari,则认为它是object因为variablesis IEnumerable,而不是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); 
}
于 2013-10-21T08:44:33.793 回答
2

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

vari = (DictionaryEntry)e.Current;

万一object你会被强制转换为对象,从而调用KeyValue非编译。

来自C# Spec 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 关于这个主题的文章为什么 foreach 循环会默默地插入“显式”转换?

于 2013-10-21T08:45:58.803 回答