考虑以下代码:
var variables = System.Environment.GetEnvironmentVariables();
foreach (DictionaryEntry vari in variables)
{
Console.WriteLine(vari.Key);
Console.WriteLine(vari.Value);
}
工作正常。由于variables
为IDictionary
,因此它由DictionaryEntry
,object Key
和object Value
组成。
为什么我不能输入foreach(var vari in variables)
?它给了我
error CS1061: 'object' does not contain a definition for 'Key/Value'...
这似乎很奇怪,我找不到这种行为的原因。 DictionaryEntry
是struct
,但我可以对List<DictionaryEntry>
进行迭代。我当然理解IDictionary
不是通用的,但manual表示它包含DictionaryEntries
,因此应该可以使用var
...
答案 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
,因为variables
为IEnumerable
,而不是{{1} }}
IEnumerable<DictionaryEntry>
答案 2 :(得分:2)
foreach循环包含显式强制转换。所以你会在foreach循环中得到一些东西:
vari = (DictionaryEntry)e.Current;
如果object
您将被转换为对象,从而调用Key
和Value
非编译。
来自 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?