Managed Extensions: Versioning Collection Classes

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.

In the previous two articles, I've illustrated how to make your C++ classes enumerable and how to add sorting capabilities to those classes. This article discusses the issue of versioning. Put simply, versioning your collections becomes important if the data held by the collection can be modified while a client is enumerating that data. Here's an example of the problem using the same Article class example I've been using throughout this series on programming collection classes with Managed Extensions:

  1. Client code instantiates an ArticleCollection object.
  2. Client code then requests an enumerator for that collection.
  3. The ArticleCollection instantiates an ArticleEnumerator object, passing to it the current data held in an internal member. This is done so that multiple clients can retrieve enumerators from the same collection object.
  4. Client code changes the collection's data—by adding or removing an item. Because the enumerator object's data is for enumeration purposes only, it is now out of synch with the collection data. Therefore, the client code is now using an obsolete enumerator.

The following steps demonstrate a technique that a member of the Microsoft .NET Common Language Runtime (CLR) team showed me—a technique that he told me they use internally.

  1. Define an internal member of the collection class (the IEnumerable implementing class) that tracks the current version number. The following code snippet assumes, as a starting point, the article.h file used in the "Managed Extensions: Sorting Your Collection Classes" article:
  2. __gc class ArticleCollection : public IEnumerable
    {
    ...
       static int version = 0;
    
  3. Increment the version number member from any collection method that would render the collection invalid. For example, a method that allows client code to add data to the collection would obviously invalidate the collection for any client that is currently enumerating that object:
  4. __gc class ArticleCollection : public IEnumerable
    {
    ...
    public:
       void Add()
       {
          // If data is changed, update version so that any
          // outstanding enumerators are now out of data!
          version++;
          // perform add
       }
    
  5. Update the collection class's GetEnumerator method to pass the current version to the enumerator class's constructor. This is done so that you can track the version number at the enumerator object level:
  6. __gc class ArticleCollection : public IEnumerable
    {
    ...
    public:
       IEnumerator* GetEnumerator()
       {
          return dynamic_cast<IEnumerator*>(new
                 ArticleEnumerator(this, version));
       }
    
  7. Update the enumerator object's constructor to also receive the version number and store it at the object instance level:
  8. __gc class ArticleEnumerator : public IEnumerator
    {
    ...
    public:
       ArticleEnumerator(ArticleCollection* collection,
                         int version)
       {
          this->collection = collection;
          this->version = version;
          position = -1;
       }
    protected:
       int version;
    
  9. Add a method to the enumerator class to throw an exception if the client attempts to access an obsolete enumerator's data:
  10. __gc class ArticleEnumerator : public IEnumerator
    {
    protected:
       void VerifyVersion()
       {
          if (version != collection->version)
            throw new InvalidOperationException(S"Data out of sync. "
                                                S"Need to reacquire
                                                enumerator");
    }
    
  11. Finally, update the enumerator class's get_Current, MoveNext, and Reset methods to call the VerifyVersion method before attempting to perform their work:
  12. __gc class ArticleEnumerator : public IEnumerator
    {
    public:
       bool MoveNext()
       {
          VerifyVersion();
          ...
       }
    
       __property Object* get_Current()
       {
          VerifyVersion();
          ...
       }
    
       void Reset()
       {
          VerifyVersion();
          ...
       }
    };
    

Maintaining a Boolean Member to Indicate a "Dirty" Collection

I used a numeric field to track the version number associated with a given enumerator object. I did this to handle scenarios in which the client might retrieve multiple enumerator objects from a single collection object. As a result, each time the data is changed, the client code needs to re-acquire each enumerator object that is made obsolete. Depending on the complexity of your needs, you could use a simple boolean member to indicate whether or not the collection is "dirty" or changed. The main difference is that, if you use such a technique, you would be marking the collection dirty for all clients of a particular instance of the collection. So, it really comes down to the needs of your applications.

Looking Ahead

The past three (3) articles have illustrated the technique of creating two additional classes to the class that you're enumerating—a collection class (IEnumerable-derived) and an enumerator class (IEnumerable-derived). The next article will show how you can combine these two interfaces into a single class and explains when you would want to do that and when it would not be a good idea.



About the Author

Tom Archer - MSFT

I am a Program Manager and Content Strategist for the Microsoft MSDN Online team managing the Windows Vista and Visual C++ developer centers. Before being employed at Microsoft, I was awarded MVP status for the Visual C++ product. A 20+ year veteran of programming with various languages - C++, C, Assembler, RPG III/400, PL/I, etc. - I've also written many technical books (Inside C#, Extending MFC Applications with the .NET Framework, Visual C++.NET Bible, etc.) and 100+ online articles.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Companies undertaking an IT project need to find the right balance between cost and functionality. It's important to start by determining whether to build a solution from scratch, buy an out-of-the-box solution, or a combination of both. In reality, most projects will require some system tailoring to meet business requirements. Decision-makers must understand how much software development is enough and craft a detailed implementation plan to ensure the project's success. This white paper examines the different …

  • On-demand Event Event Date: February 12, 2015 The evolution of systems engineering with the SysML modeling language has resulted in improved requirements specification, better architectural definition, and better hand-off to downstream engineering. Agile methods have proven successful in the software domain, but how can these methods be applied to systems engineering? Check out this webcast and join Bruce Powel Douglass, author of Real-Time Agility, as he discusses how agile methods have had a tremendous …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date