Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enumerate an IDictionary.Keys collection which is an ICollection<T>

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

like image 648
Chris Avatar asked Dec 04 '22 14:12

Chris


2 Answers

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.

like image 168
Rex M Avatar answered Dec 06 '22 04:12

Rex M


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.

like image 39
Jon Seigel Avatar answered Dec 06 '22 02:12

Jon Seigel