Share via


Bookmark Collection: Implementing the Enumerator

The test that I'm going to implement this time is the following:

Add 3 Bookmarks, call GetEnumerator and verify that the 3 Bookmarks are enumerated

3 seems excessive, I think 2 will be sufficient; here is the test:

[Test]
public void EnumeratorContainsOnlyAddedItems()
{
    collection.Add(exampleDotComLabel, exampleDotComUri);
    collection.Add(exampleDotNetLabel, exampleDotNetUri);

    IEnumerator<KeyValuePair<string, Uri>> enumerator =
collection.GetEnumerator();

    Assert.IsTrue(EnumeratorContains(enumerator,
        exampleDotComLabel, exampleDotComUri));
    Assert.IsTrue(EnumeratorContains(enumerator,
exampleDotNetLabel, exampleDotNetUri));
}

And to get this to work I need to implement the test helper – EnumeratorContains.

private bool EnumeratorContains(
IEnumerator<KeyValuePair<string, Uri>> enumerator,
string label, Uri uri)
{
while (enumerator.MoveNext())
{
KeyValuePair<string, Uri> bookmark = enumerator.Current;
if (bookmark.Key.Equals(label) &&
bookmark.Value.Equals(uri))
return true;
}

return false;
}

Don’t you just love that generics syntax. I am starting to get the C++ template shivers again. There is a big problem with this code but lets get it to work before starting to fix it. Here is the implementation I need to get the test to pass:

public IEnumerator<KeyValuePair<string, Uri>> GetEnumerator()
{
return dictionary.GetEnumerator();
}

This is simple enough, and the tests pass. Now that it works what was that issue? It seems to me that returning a KeyValuePair<string, Uri> exposes too much of the underlying storage mechanism. What if someone wanted to come along and change it? You are potentially looking at a change to the interface or if you are unwilling to do that you would have to adapt to the new interface. Also, the syntax as I mentioned before is not the prettiest. My solution to this problem is to introduce a class called Bookmark. Let’s rewrite the test.

[Test]
public void EnumeratorContainsOnlyAddedItems()
{
collection.Add(exampleDotComLabel, exampleDotComUri);
collection.Add(exampleDotNetLabel, exampleDotNetUri);

IEnumerator<Bookmark> enumerator = collection.GetEnumerator();

Assert.IsTrue(EnumeratorContains(enumerator,
exampleDotComLabel, exampleDotComUri));
Assert.IsTrue(EnumeratorContains(enumerator,
exampleDotNetLabel, exampleDotNetUri));
}

I left out the EnumeratorContains method as an exercise for you, the reader. Here is the corresponding implementation:

public IEnumerator<Bookmark> GetEnumerator()
{
foreach (KeyValuePair<string, Uri> bookmark in dictionary)
yield return new Bookmark(bookmark.Key, bookmark.Value);
}

and the simplest Bookmark class implementation:

public

class Bookmark
{
private string label;
private Uri uri;

public Bookmark(string label, Uri uri)
{
this.label = label;
this.uri = uri;
}

public string Label
{
get { return label; }
}

public Uri Uri
{
get { return uri; }
}
}

When I compile and run this the tests pass. I don’t like that we have to create a new Bookmark to enumerate over the bookmarks so thats where I will pick up next time.

Comments