Our 'Non-Collection' Class
The class I will show you how to create represents a fiscal month and I'll call it FiscalMonth. You can find the complete code in Listing 1
(VB.NET) and the equivalent Listing 2
(C#). As many of you know from the companies you work for, a fiscal month does not always represent a calendar month, in fact it rarely does. The January fiscal month may very well start with December 27th of the previous year and end on January 26th, while the February fiscal month may begin on January 27th and end on March 1st. If you were to design a class as a standard collection class, you would expect it to contain a list with all the dates in the fiscal month. Instead, the FiscalMonth class will have only two primary properties: MonthBeg
each declared as a DateTime type. Of course, this class can contain as much data and functionality as your application needs. Your goal is to be able to iterate through the dates in the fiscal month.
In VB .NET:
For Each o_Day As DateTime In o_FiscalMonth
' anything can go here
foreach(DateTime o_Day in o_FiscalMonth)
// anything can go here
I'll explain the enumerating functionality in this class a little later. Right now I'll start with the enumerator that controls the iteration.
Creating the Enumerator
The first interface I'll examine is IEnumerator. This interface defines the actual enumerator that is returned by each iteration. To make this interface more reusable, I'll implement this interface in its own classa design concept that I live and die by. However, the functionality contained in the enumerator class you'll learn how to create could very well be contained in the FiscalMonth class itself. By keeping it separate, you can reuse the functionality in future FiscalYear and FiscalQuarter classes. You can view the complete DayEnumerator class in Listing 3
and Listing 4
. A key factor of the DayEnumerator class is the fact that it needs to know the beginning and end of the iteration allowed. If you included the functionality in this class in the FiscalMonth class, you would already have access to the required information, but since it is separate you need two properties to represent MonthBeg
. You'll see later how these properties get filled. So far it seems pretty simple. In fact it looks like there are two very similar classeswell that ends now.
By implementing the IEnumerator interface in the class you add two methods (MoveNext
and a property (Current)
to it. First, you need to add two variables to your enumerator class: one to store the current date in the iteration and the other to use as a counter. Remember that you aren't containing a list or collection here so you need to keep your own counter and value.
In VB .NET:
Private i_Pos As Integer
Private dt_Date As DateTime
private int i_Pos;
private DateTime dt_Date;
variable contains your position in the iteration, zero being the first value (or the equivalent of the MonthBeg
value), and the last value is the difference of the begin and end dates. The dt_Date
variable will hold the current date during the iteration.
VB.NET's For Each
statement is designed to work with objects that implement the two enumerating interfaces. When you first use a For Each
statement, it looks at the object being iterated and, provided you have implemented IEnumerable properly (more on this in a minute), it figures out what enumerator object to use (your DayEnumerator class). You would use the Reset
method if you want to reset the iteration at any point. You can call it manually from the constructor of the enumerator object so you can manage this code from just one place. You will also notice that I assigned i_Pos
an initial value of 1. Later, when you construct the MoveNext
method, you will see where it gets its zero value.
In VB .NET:
Public Sub New()
Public Sub Reset() Implements _
i_Pos = -1
public void Reset()
i_Pos = -1;
The For Each
statement then begins to move through the iteration using the enumerator object's MoveNext
In VB .NET:
Public Function MoveNext() As Boolean Implements _
If i_Pos = -1 Then
i_Pos += 1
dt_Date = _FromDate
If dt_Date < _ToDate Then
i_Pos += 1
dt_Date = _FromDate.AddDays(i_Pos)
public bool MoveNext()
if(i_Pos == -1)
dt_Date = _FromDate;
if(dt_Date < _ToDate)
dt_Date = _FromDate.AddDays(i_Pos);
Read closely through this code and you will see that it is actually quite simple. If the counter is set to its initial value, you set the value variable dt_Date
, to the equivalent of the MonthBeg
variable, and then you simply increase the counter. On all subsequent passes, you set the value variable to a date equal to the MonthBeg
date plus the number of days contained in the counter. You wrap this in a condition check for the current date value against the MonthEnd
value. The method returns a Boolean value that tells the iteration mechanism whether or not to continue onto the next one.
Remember earlier I said that the .NET Framework's For Each
mechanism first looks into the object being iterated. Here it found the enumerator I just explained. Now let's go back and look at that very object that you're trying to iterate though, FiscalMonth (Listing 1
and Listing 2
FiscalMonth must implement the other interface, IEnumerable. This interface implements just one method, GetEnumerator
, which returns an object of type IEnumerator. Whoa! Slow downhow did you get back there again? I know, it looks a bit confusing at first, but here's what is going on: the For Each
mechanism in the .NET Framework looks in this class first. When it confirms that this class properly implements the necessary interface, it looks at the GetEnumerator
method and it retrieves the object to use as the enumerator, in this case that's the DayEnumerator class, which as you've already seen, implements the IEnumerator interface.
In VB .NET:
Public Function GetEnumerator() As _
System.Collections.IEnumerator Implements _
Return CType(o_DayEnumerator, IEnumerator)
public IEnumerator GetEnumerator()
object is contained by the FiscalMonth class and is declared at the class level in order to keep its state throughout the entire life cycle of the instance of FiscalMonth you're working with.
In VB .NET:
Private o_DayEnumerator As DayEnumerator = _
private DayEnumerator o_DayEnumerator =
Look at the code in Listing 1
and you will also see that setting the MonthBeg
values in either the property declarations or the constructor will filter them down to the MonthBeg
properties also defined in the DayEnumerator class.
In this example of custom enumerators, with the little amount of code you've used, it would seem that with the exception of the interface implementation code, the two classes you've created are nearly identical but that is not the case. The FiscalMonth class may very well contain much more data and functionality than you've put into it, while the DayEnumerator class would stay pretty much the same. If your FiscalMonth class does not contain much more information or functionality, you could certainly implement the IEnumerator interface in the class, and place the Reset
, and MoveNext
methods there. You can then use the MonthBeg
properties of the FiscalMonth itself during your iteration functionality.