Derik Whittaker

Syndication

News


Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Howto: Creating and Using Custom Collection Enumerators

Another one of the cool new features with .net 2.0 is the ability to create your own custom enumerators for enumerating through collections.  I am sure that most of us have had collections that we wanted to loop through, looking for a specific type of item for one reason or another.  There are many ways that looping through a collection can be accomplished, but I would say the 2 most common is by using a for-loop or a foreach loop.  If you have ever used the foreach loop than you have used the default enumerator built into the collection.  Did you know that you can create your own enumerator to meet you specific business need?

 

Lets say you have a collection of Business Entities that you would like to loop though and return only the entities for a given property value.  In my case I have added a Size property to my entities so we can filter on that. 

In this example I am going to create 3 classes:

  1. BusinessEntity -- This is the actual business entity class that contains all my properties/methods
  2. BusinessEntityCollection -- The collection of the business entities.  This will also have my Custom Enumerator
  3. BusinessEntityCollectionSmallEnumerator -- The enumeration class needed for the Custom Enumerator 

Here is the code for a standard For-Each Loop:

 

            StringBuilder output = new StringBuilder();

            foreach ( BusinessEntity entity in entities )

            {

                if ( entity.Size == BusinessEntity.EntitySize.Small )

                {

                    output.AppendLine( entity.Name );   

                }

 

            }

 

            MessageBox.Show( output.ToString() );

 

Now Here is the code Using the Custom Enumerator:

 

            StringBuilder output = new StringBuilder();

            foreach ( BusinessEntity entity in entities.GetSmallEntityEnumerator() )

            {

                output.AppendLine( entity.Name );

            }

 

            MessageBox.Show( output.ToString( ) );

 

If you notice, the only real difference in the Custom Enumerator example is the removal of the 'if' check. Although this may not seem like a big thing, it is. Now you can loop through this collection type anywhere in your app and not have to worry about performing that check.... It is done for you.


Ok, so lets now look at the code needed to create the custom enumerator. The first thing you are going to need to do is create a class that implements the IEnumerator and IEnumerable interfaces. These two interfaces will give you what you need for the enumeration.


Below is the core Enumeration code needed to make this work. I have included my sample project with this post for the full working solution.

        /// <summary>

        /// Will get the actual current entity out the the collection.

        /// </summary>

        public BusinessEntity Current

        {

            get

            {

                if ( ( InUseIndex < 0 ) | ( InUseIndex >= list.Count ) )

                {

                    throw new InvalidOperationException( "Overflow Exception" );

                }

 

                //  Return the current item

                return list[ InUseIndex ];

            }

        }

        /// <summary>

        /// Will perform the move logic during the enumeration loop

        /// </summary>

        /// <returns></returns>

        public bool MoveNext()

        {

            bool returnFlag = false;

 

            //  Continue to increment the internal position if still within the collection bounds and

            //  the entity is not active.

            for ( Int32 index = MoveNextStartIndex; index <= list.Count - 1; index++ )

            {

                // Add your custom logic here for the enumerator

                //  If you were checking 'deleted' or something do it here

                if ( list[ index ].Size == BusinessEntity.EntitySize.Small )

                {

                    InUseIndex = index;

                    MoveNextStartIndex = index + 1;

                    returnFlag = true;

                    break;

                }

 

            }

 

            return returnFlag;

        }

In order to use this Custom Enumeration, you will need to create a colleciton object. I have my collection impelment the IList interface. After you have implemented all the required interface methods for IList, you will need to create one more method to implement your Custom Enumerator.


Here is the code needed to implement the Custom Enumeration in your Collection:

        public BusinessEntityCollectionSmallEnumerator<BusinessEntity> GetSmallEntityEnumerator()

        {

            //  Create the active enumerator

            BusinessEntityCollectionSmallEnumerator<BusinessEntity> sizeEnumerator;

 

            sizeEnumerator = new BusinessEntityCollectionSmallEnumerator< BusinessEntity >( this );

 

            //  Return the enumerator

            return sizeEnumerator;

        }

You now have enough to create and implement your own custom enumeration.  Be sure to download the working solution in order to view the full project.  This will also show you some of the 'Implemented' code that I did not talk about in the post.
 


Posted 10-05-2006 6:48 AM by Derik Whittaker
Filed under: ,

[Advertisement]

Comments

mgrzeg wrote re: Creating and Using Custom Collection Enumerators
on 10-05-2006 10:17 AM

yield does most of this job for you :)

public class BusinessEntityCollection

{

...

public IEnumerator< BusinessEntity > GetSmallEntityEnumerator()

{

 foreach(BusinessEntity be in list)

 {

  if(be.Size == BusinessEntity.EntitySize.Small)

  yield return be;

 }

}

}

and it's done :) the rest is compiler job: actually it does mostly the same what you did in your sample code -> additional class, movenext methods, and so on.

quiz: who first proposed 'yield' keyword in his language and when was it? :)

Jason wrote re: Creating and Using Custom Collection Enumerators
on 10-05-2006 10:29 AM

Why not just use iterators? No additional classes needed and you can have as many as you want in the collection class.

Michal Grzegorzewski wrote re: Creating and Using Custom Collection Enumerators
on 10-05-2006 10:43 AM

I'm not sure why my previous comment hasn't been added, so I decided to join your community and add another one as a loggedon user. It was mostly as follows:

---

yield does most of this job for you!

   public class BusinessEntityCollection : IList<BusinessEntity>

   {

   ...

       public IEnumerator<BusinessEntity> GetEntityEnumeratorWithSize(BusinessEntity.EntitySize size)

       {

           foreach (BusinessEntity be in list)

           {

               if (be.Size == size)

                   yield return be;

           }

       }

  }

and then use it

           foreach ( BusinessEntity entity in entities.GetEntityEnumeratorWithSize(BusinessEntity.EntitySize.Small) )

           {

               output.AppendLine( entity.Name );

           }

compiler does mostly the same as you did - prepares additional class implementing IEnumerator with small automata (based on labels and gotos from switch block)

Michal Grzegorzewski wrote re: Creating and Using Custom Collection Enumerators
on 10-05-2006 11:06 AM

sorry for small error in code: instead of

public IEnumerator<BusinessEntity> GetEntityEnumeratorWithSize(BusinessEntity.EntitySize size)

there should be

public IEnumerable<BusinessEntity> GetEntityEnumeratorWithSize(BusinessEntity.EntitySize size)

I was so involved in the name of the method, that forgot of the type which sholud be used.

Additionaly compiler-generated class implements also IEnumerable of specific type.

Derik Whittaker wrote re: Creating and Using Custom Collection Enumerators
on 10-05-2006 12:06 PM

This is cool,  i can say that i have yet to use the yield key word.  I will have to look into this more.

Thanks for the information.

El Guapo wrote re: Creating and Using Custom Collection Enumerators
on 10-05-2006 9:31 PM

Yes, this is the old (.NET 1.1) way of doing enumerators.

The whole new enumerator stuff in .NET 2.0 lets you use a statement "yield return x;" which, as the commenter above noticed, does all the work for you.

Peter Mescalchin wrote re: Creating and Using Custom Collection Enumerators
on 10-06-2006 11:26 AM

Very smart code.  And mgrzeg enhancements with the funky yield keyword are just the icing on the cake! You have to love generics!

Chris wrote re: Howto: Creating and Using Custom Collection Enumerators
on 03-01-2007 4:13 PM

Yes, yield does the job in this instance. So, let's forget about Size for a moment.

Let's say instead that you have an internal set of db rows, and you want to inflate them as-needed while looping through the collection (so you don't do all of the inflation if it's not needed, and you don't want the resultant objects to contain an internal instance of the row).  A custom enumerator is the perfect place to do that. Current/MoveNext tracks what record you're on, and inflates to your custom object.

shirley hills wrote re: Howto: Creating and Using Custom Collection Enumerators
on 04-08-2008 12:42 PM

i think it will be commendable to have you add the qualities of a good enumerator on the site.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)