August 21, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Managed Extensions: Adding Enumeration to Your Classes

  • August 23, 2004
  • By Tom Archer
  • Send Email »
  • More Articles »

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 addition to providing thousands of classes and types that enable us to create richer and more robust class hierarchies, the .NET BCL (Base Class Library) also defines many interfaces that enable the class writer to incorporate powerful features simply by implementing a given interface and its members. This concept, called interface-based programming, creates a contract between the implementing class and its clients, such that the client can be assured of what to expect from the class. The BCL even provides special operators and methods, such that client code can verify that a given interface is implemented before attempting to use it.

As an example, say that you define a class that implements an interface called ISomeInterface. Using Visual C++ and Managed Extensions, the client could then use the __typeof operator and Type::GetInterface method to verify that the ISomeInterface interface is implemented:

Type* t = __typeof(myVar);
if (t->GetInterface(S"ISomeInterface"))
{
  ...
If the client is a C# application, this same verification would look like the following—where the C# is operator is used:
if (myVar is IEnumerable)
{
 ...
Having said that, one set of interfaces that you'll find quite useful is the IEnumerable and IEnumerator interfaces. These interfaces enable you to quickly and easily define your classes, such that client code can enumerate them in a type-safe and object-specific manner. This article presents step-by-step instructions for performing this task.

Implementing the IEnumerable and IEnumerator Interfaces

As a demo project, say you have a class called Article that contains some basic fields, such as title, author, and category:
__gc class Article
{
public:
  Article(String* title, String* author, String* category)
  {
    this->title = title;
    this->author = author;
    this->category = category;
  }

protected:
  String* title;
  String* author;
  String* category;

public:
  __property String* get_Title() { return this->title; }
  __property String* get_Author() { return this->author; }
  __property String* get_Category() { return this->category; }
};

In order to provide enumeration for a collection of Article objects, you could perform the following steps:

Please note that in order to make the following steps more clear in terms of where to insert code, I've duplicated code in some cases. For that reason, any step that includes previously shown code will display the new code to be inserted in bold.

  1. Define a class that will act as the collection class for the main class that you wish to be enumerable (Article, in this example). Derive it from the IEnumerable interface, which lets clients know that the class can be enumerated:
    __gc class Article
    {
    ...
    };
    
    
    __gc class ArticleCollection : public IEnumerable
    {
    };
    
    
  2. Add a member variable that will contain the collection of objects that can be enumerated. For example, the ArticleCollection variable holds a collection of Article objects, so you need to decide in which type of member to hold those objects. This example uses an ArrayList type:
    __gc class ArticleCollection : public IEnumerable
    {
    protected:
      ArrayList* articles;
    
  3. The IEnumerable interface defines the GetEnumerator method, returns the enumerator object that the client code uses to enumerate the collection. Therefore, implement a GetEnumerator method. In the following GetEnumerator method implementation, the code instantiates a new enumerator object and returns that object casted to the base IEnumerator type:
    __gc class ArticleCollection : public IEnumerable
    {
    ...
    public:
       IEnumerator* GetEnumerator()
       {
          return dynamic_cast<IEnumerator*>(
                 new ArticleEnumerator(this));
       }
    
    public:
       __gc class ArticleEnumerator : public IEnumerator
       {
       };
    ...
    
  4. Now you need to define the enumerator class's constructor. As you can see from the previous step, the enumerator class is instantiated by passing the current collection instance object to its constructor. This is done because one collection object might be enumerated by multiple clients. Therefore, not only might the data be different for each client (depending on when that client requests the enumerator object), but each client will be positioned within the collection (via the enumerator object) at different positions.

    Therefore, the enumerator class contains an internal parent collection object as well as a position member to keep track of where the client is when enumerating the collection. Here's how you would define and initialize these:

    __gc class ArticleEnumerator : public IEnumerator
    {
    protected:
      ArticleCollection* collection;
      int position;
    
    public:
      ArticleEnumerator(ArticleCollection* collection)
      {
        this->collection = collection;
        position = -1;
      }
    ...  
    
  5. At this point, you have the basic framework for an enumerable collection. All that is left is implementing the IEnumerator methods: MoveNext, Current, and Reset.

    • The MoveNext method simply increments the current position value. As it is standard practice to initialize the current position to -1, the client should always call this method first (before attempting to use the Current property).
    • Use the Current property (get_Current C++ function) to retrieve the element associated with the current position value. This property must first ensure that the current position is valid. If it is not—such as if it is less than 0 (indicating the first element) or greater than the number of elements—it is customary to throw an exception of type InvalidOperationException. The exact mechanics of how to return the current object are going to be specific to how the objects are stored within the collection. In the next step, you'll see an example of how to return an element from an ArrayList type.
    • Use the Reset method to simply reset the current position to its initialized value of -1:
    • __gc class ArticleEnumerator : public IEnumerator
      {
      ...
      public:
        bool MoveNext()
        {
          position++;
          return (position < collection->articles->Count);
        }
      
        __property Object* get_Current()
        {
          if (0 > position
          || position >= collection->articles->Count)
            throw new InvalidOperationException();
      
          return collection->articles->Item[position];
        }
      
        void Reset()
        {
          position = -1;
        }
      ...
      




Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel