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...
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);
}
}
}
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With