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