Click here to Skip to main content
Click here to Skip to main content
Go to top

The Enumerable Enumerator

, 2 Nov 2006
Rate this:
Please Sign up or sign in to vote.
Iterate over your enums, and other things to do with enums.

Introduction

The Enumerable Enumerator is something I threw together to address a need to iterate and sequence through enumerations. I find this class useful when working with hardware and dealing with such things as bit fields. I also found application for this class in managing state and sequencing through states. For example, I may have an enumeration of bits:

enum AlarmBits
{
  Door=1,
  Window=2,
  Security=4,
  CoinJam=8,
  BillJam=16,
}

and I simply want to iterate through the enumeration to test each bit.

Or, I may have a set of states:

enum States
{
  Slow,
  Medium,
  Fast
}

and in this case, I want to be able to change state in a forward or backwards direction but not exceed the state limits. As a side benefit, I found that using the methods in this class resulted in more robust application code, as I could change the enumeration without having to go back and update the application code. If I add an alarm bit or a state, I'm only changing the enumeration, not the code that implements iterating through the enumeration or managing a state.

The Code

The class Enumerator consists of the following properties and methods. All the examples use this enum:

enum Test
{
  A=1,
  B=2,
  C=3,
  D=4,
}

You will note that all the methods are static because they operate on the enumeration type as opposed to an instance of the enumeration. The use of generics improves type checking (for example, the current value and the minimum value must be of the same type) and avoids downcasting of the returned value in the application code.

First

Returns the first value in the enumerator. As with all these methods, the enumerations are sorted by value, which may not be the same as the ordinal value.

public static T First
{
  get { return ((T[])Enum.GetValues(typeof(T)))[0]; }
}

Example:

Console.WriteLine("First = " + Enumerator<Test>.First);

The output is:

First = A

Last

Returns the last enumeration value.

public static T Last
{
  get 
  {
    T[] vals = (T[])Enum.GetValues(typeof(T));
    return vals[vals.Length-1]; 
  }
}

Example:

Console.WriteLine("Last = " + Enumerator<Test>.Last);

The output is:

Last = D

PreviousOrFirst

This method returns the previous enumeration value, or returns the first value if the enumeration cannot be decremented further. I find this method, and the corresponding NextOrLast method, useful to work with enumerators that represent state.

public static T PreviousOrFirst(T val)
{
  T[] vals = (T[])Enum.GetValues(typeof(T));
  int idx = Array.FindIndex<T>(vals, delegate(T v) { return v.Equals(val); });
  
  return idx == 0 ? vals[0] : vals[idx - 1];
}

Example:

Console.WriteLine("Previous Or First of A = " + 
    Enumerator<Test>.PreviousOrFirst(Test.A));
Console.WriteLine("Previous Or First of B = " + 
    Enumerator<Test>.PreviousOrFirst(Test.B));

Outputs:

Previous Or First of A = A
Previous Or First of B = A

NextOrLast

This method is the compliment of the PreviousOrFirst method.

public static T NextOrLast(T val)
{
  T[] vals = (T[])Enum.GetValues(typeof(T));
  int idx = Array.FindIndex<T>(vals, delegate(T v) { return v.Equals(val); });

  return idx == vals.Length-1 ? vals[idx] : vals[idx + 1];
}

Example:

Console.WriteLine("Next Or Last of C = " + 
  Enumerator<Test>.NextOrLast(Test.C));
Console.WriteLine("Next Or Last of D = " + 
  Enumerator<Test>.NextOrLast(Test.D));

The output is:

Next Or Last of C = D
Next Or Last of D = D

PreviousBounded

It seemed reasonable to also supply a couple methods that are bounded by a supplied enum value rather than the first or last enum value.

public static T PreviousBounded(T val, T min)
{
  T[] vals = (T[])Enum.GetValues(typeof(T));
  int curIdx = Array.FindIndex<T>(vals, delegate(T v) {return v.Equals(val);});
  int minIdx = Array.FindIndex<T>(vals, delegate(T v) {return v.Equals(min);});
  return curIdx <= minIdx ? vals[minIdx] : vals[curIdx - 1];
}

Example:

Console.WriteLine("Enumerator low limit (C->B) = " + 
  Enumerator<Test>.PreviousBounded(Test.C, Test.B));
Console.WriteLine("Enumerator low limit (B->B) = " + 
  Enumerator<Test>.PreviousBounded(Test.B, Test.B));
Console.WriteLine("Enumerator low limit (A->B) = " + 
  Enumerator<Test>.PreviousBounded(Test.A, Test.B));

The output is:

Enumerator low limit (C->B) = B
Enumerator low limit (B->B) = B
Enumerator low limit (A->B) = B

NextBounded

public static T NextBounded(T val, T max)
{
  T[] vals = (T[])Enum.GetValues(typeof(T));
  int curIdx = Array.FindIndex<T>(vals, delegate(T v) { return v.Equals(val); });
  int maxIdx = Array.FindIndex<T>(vals, delegate(T v) { return v.Equals(max); });

  return curIdx >= maxIdx ? vals[maxIdx] : vals[curIdx + 1];
}

Example:

Console.WriteLine("Enumerator high limit (B->C) = " + 
  Enumerator<Test>.NextBounded(Test.B, Test.C));
Console.WriteLine("Enumerator high limit (C->C) = " + 
  Enumerator<Test>.NextBounded(Test.C, Test.C));
Console.WriteLine("Enumerator high limit (D->C) = " + 
  Enumerator<Test>.NextBounded(Test.D, Test.C));

The output is:

Enumerator high limit (B->C) = C
Enumerator high limit (C->C) = C
Enumerator high limit (D->C) = C

Enumerating

The original goal was simply to be able to iterate over the enumeration (changed as per the suggestions in the comments):

public static IEnumerable<T> Items()
{
  return (T[])Enum.GetValues(typeof(T));
}

Example:

foreach (Test t in Enumerator<Test>.Items())
{
  Console.WriteLine(t + " = " + Convert.ToInt32(t));
}

The output is:

A = 1
B = 2
C = 3
D = 4

Conclusion

So, it's a simple class but it was fun to write, provides some useful functionality, and helps to decouple the enumeration implementation from the application logic, which is something I always find useful.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Marc Clifton

United States United States
Marc is the creator of two open source projets, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website, www.marcclifton.com, where you will find many of his articles and his blog.
 
Marc lives in Philmont, NY.

Comments and Discussions

 
GeneralSuggestion PinmemberSteve Hansen2-Nov-06 6:31 
GeneralRe: Suggestion PinmemberChristian Klauser2-Nov-06 6:49 
GeneralRe: Suggestion PinprotectorMarc Clifton2-Nov-06 8:23 
GeneralRe: Suggestion PinprotectorMarc Clifton2-Nov-06 8:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 2 Nov 2006
Article Copyright 2006 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid