Managed Extensions: Combining IEnumerable and IEnumerator
Welcome to this week's installment of .NET Tips & Techniques! Each week, award-winning Architect and Lead Programmer Tom Archer demonstrates how to perform a practical .NET programming task using either C# or Managed C++ Extensions.
Previous installments of the .NET Tips & Techniques series covered using types defined in the .NET Collection namespace to make classes both enumerable and sortable from Visual C++ and Managed Extensions applications. Each article employed the technique of creating two additional classes to the class being enumerated: a collection class (IEnumerable-derived) and an enumerator class (IEnumerator-derived). This article explains why the .NET BCL (Base Class Library) team chose to define two interfaces that are used together to achieve enumerability and how—in specific scenarios—you can define a single class that implements both interfaces.
Why Two Interfaces?Why doesn't the IEnumerable interface define the Current, MoveNext, and Reset members, thereby eliminating the need for the IEnumerable::GetEnumerator method and the IEnumerator interface? That way, the collection class could simply provide public accessor methods to the data. Although this technique sounds reasonable at first blush, it has drawbacks.
One such drawback is evident if a single collection object can have multiple clients and its data can be modified (especially if records can be added or deleted) while the data is being enumerated. You could, of course, circumvent this particular problem by creating a "data object" internal to the collection object for each client. Then the problem would be associating each client with its respective data object—typically done through an attach/detach method pair. The client then would need to identify itself using some sort of ID (probably returned from an attach method) in order to retrieve its data.
As you can see, this solution heads down a slippery slope of diminishing returns, generating more work than it alleviates. This is not even mentioning the fact that you would forfeit the generic solution afforded you via the IEnumerable/IEnumerator interface pair. With these interfaces, the snapshot of the collection data that existed when the client requested the enumerator object is copied to the enumerator object and becomes available via the standard methods: Current, MoveNext, and Reset.