I hope i don't get slammed for asking something so basic. I could Google for the answer, but I want to hear something that's not from a textbook.
I'm writing a unit test to verify that my IDictionary
Keys are sequential.
Since the Keys
property is an ICollection<T>
, I want to enumerate over the collection and print the Key values to the console.
When attempting to print the Key values using a simple for
loop:
for (int i = 0; i < unPivotedData.Count; i++)
{
Console.WriteLine(unPivotedData.Keys[i]);
}
I received the following compile error:
Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.ICollection<int>'
However, when I used the foreach
loop:
foreach(int key in unPivotedData.Keys)
{
Console.WriteLine(key);
}
Everything worked just fine.
I understand what an indexer does and how it's implemented, but how does the foreach
work? I don't understand how the foreach
can work yet the for
results in a compiler error.
Am I missing a fundamental of Enumeration here?
Cheers!
EDIT:
Furthermore, is there a performance difference between the two? I know I cannot use for
with an IDictionary but if I'm using an IList, I can. Does the for
move quicker than the foreach
or is the performance gain negligible
foreach
simply needs IEnumerable
; whereas for(a,b,c)
(in your case) requires an indexer property.
When you call foreach
on an object that implements IEnumerable
, under the hood it calls GetEnumerator()
, which returns an IEnumerator object. That object implements a few members like MoveNext()
and Current
. Each iteration of the foreach
is actually calling MoveNext()
, which returns true
if the enumerator can move forward, and false if it can't (reached the end).
The key here is that an object which implements IEnumerable
does not know how many items it has, the index of the item it's on, etc. All it knows is that it can move forward to the next item, return that as the current item, until it runs out of items.
A for(a,b,c)
loop, on the other hand, will essentially loop forever - i.e. it is not constrained by the collection you might happen to be iterating over (although in practice it usually is). At the most basic level, it executes C
once each time, and checking if B
is true. If B
is true, it will loop again. If it's false, it will stop. Each time it loops, inside the loop you are probably calling object[number]
, which if your object does not have such a property, will of course fail.
The key here is that an object with an indexer supports random access - that means you can, at any point, call [arbitrary index]
and grab that one. Contrast this with IEnumerable
, which again can only access the current item.
The Keys collection is not indexed because by definition of a dictionary, ordering of the elements is not guaranteed. Which, unfortunately, also means that the purpose of the unit test you're writing is fundamentally flawed.
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