Content by Category
.NET 1.x
.NET 2.0
.NET 3.0
.NET 3.5
.NET 4.0
.NET 4.5
.NET Assemblies
.NET Framework
.NET Getting Started
Accessibility
ADO.NET
Advertorials
Agile Development
AJAX
Amazon Web Services
Analysis Services
Android
Architecture
Arduino
ASP .NET Web API
ASP.NET
ASP.NET MVC
ASP.NET WebForms
Azure
B2B (Business Integration)
BDD
Big Data
Bing
BizTalk
Book Excerpts
Build and Deploy
Business Intelligence
C#
C++
ClickOnce
Cloud Computing
Code Contracts
CODE Framework Info - non Technical
CODE on the Road!
COM+
Community
Conferences
Continuous Integration
Crystal Reports
CSLA.NET
CSS
Data
Debugger
Design Patterns
Development Process
Display Technologies
Distributed Computing
Document Database
DotNetNuke
DSL
Dynamic Languages
Dynamic Programming
Editorials
Enterprise Services ("COM+")
Entity Framework
Events
Expression Blend
F#
Fox to Fox
Frameworks
Functional Programming
Git
Graphics
HTML 5
Internet Explorer 8.0
Interviews
IOS
iPhone
Iron Ruby
Java
Java Script
JavaScript
jQuery
JSON
Lightswitch
LINQ
Linux
LUA
Mac OS X
MDX
Messaging
Metro
Microsoft Application Blocks
Microsoft Business Rules Framework
Microsoft Dynamics
Microsoft Expression
Microsoft Office
Mobile Development
Mobile PC
Mono
MsBuild
MVVM
MySQL
Network
NHibernate
node.js
NOSQL
Nuget
Object Oriented Development
Objective C
Odata
OLAP
Open Source
Opinion
Opinions
Oracle
ORM
Other Languages
Parallel Programming
Patterns
PHP
Podcasts
Post Mortem
PowerPoint
Print/Output
Prism
Product News
Product Reviews
Project Management
Prolog
Python
Q&A
Rails
Rake
Razor
Reporting Services
REST
RIA Services
Ruby
Ruby on Rails
Scheme
Search
Security
Services
SharePoint
SignalR
Silverlight
SOA
Social Networks
Software & Law
Software Business
Source Control
Speech-Enabled Applications
SQL Server
SQL Server 2000
SQL Server 2005
SQL Server 2008
SQL Server 2012
SQL Server CE/AnyWhere/Mobile/Compact
SSIS
Subversion
Sync Framework
Tablet PC
TDD
Team System
Techniques
Testing and Quality Control
TFS
Tips
TypeScript
UI Design
UML
User Groups
VB Script
VB.NET
Version Control
VFP and .NET
VFP and SQL Server
Virtual Earth
Vista
Visual Basic
Visual Basic 6 (and older)
Visual FoxPro
Visual Studio .NET
Visual Studio 11
Visual Studio 2005
Visual Studio 2008
Visual Studio 2010
Visual Studio 2011
Visual Studio 2012
Visual Studio Tools for Office
VSX
WCF
Web Development (general)
Web Services
WebMatrix
WF
Whitepapers
Windows 7
Windows 8
Windows Azure
Windows Live
Windows Phone 7
Windows Phone SDK
Windows Server
Windows Vista
WinForms
WinRT
Workflow
WPF
XAML
Xiine Documentation
XML
XNA
XSLT



LearnNow


XAMALOT
 


Sharepoint TechCon

Reader rating:
Click here to read 1 comment about this article.
Article source: CoDe (2006 - May/Jun)

‘For-Each’ Of My Own

The .NET Framework provides many new collection classes that you can iterate (for-each) through.But did you know that you can also iterate through values in any of your classes, not just those that use or inherit from collections?

This article assumes a basic knowledge of collection classes for the sake of the quick-review at the beginning.

One of the coolest things I like about the .NET Framework is the way collections are designed and handled. On the surface, you simply see many types of collections, stored in the System.Collections namespace. In fact, there is another namespace directly under this one called System.Collections.Specialized, which contains even more collection types, including one that mimics the old-fashioned Visual Basic 6.0 collection. What is not completely obvious is how these collections work internally. While this is not a tutorial on .NET collections, it’s important to understand the basics of what lies beneath the surface of all these new collection types.

Under the Hood of Collections

The heart of all the collection classes in the .NET Framework is really a set of interfaces. Which interfaces are implemented and how they are implemented determine the capabilities and behavior of any particular collection class. The two main interfaces used in collection classes are ICollection and IList. ICollection provides the functionality for item counts, enumerating, and synchronization. IList defines the methods and properties that allow the storage of items and all functionality to address that storage (add, remove, etc.). The enumeration capabilities are defined in the IEnumerable and IEnumerator interfaces. The ICollection interface, in fact, inherits from the IEnumerable interface, thus “inheriting” its interface. The way these interfaces are implemented determine if the items will be stored in a particular order (sorted list, stack, or queue), if they are hashed for storage, and other behavior. The implementation also determines how items in storage get retrieved; for example, by index, by key, ‘poped’ in the case of a Stack, ‘dequeued’ in the case of a Queue, etc.

Custom implementations of the interfaces mentioned above allow you to add some really slick functionality in your custom collection classes. In fact, the .NET Framework gives you a head start when designing your own collection classes by exposing two abstract classes, CollectionBase and DictionaryBase. These abstract classes already implement the fore-mentioned interfaces and provide the basic implementation, allowing you to further extend them for your own needs. Custom collection classes that inherit from these base classes or that are developed from scratch by totally custom-implementing all the necessary interfaces can expose their lists for iteration using standard For-Each statements (foreach in C#) as can all the standard collection types included with the .NET Framework (ArrayList, Hashtable, etc.).

In VB .NET:

For Each s_Name As String In o_MyCollectionOfNames
    ' Do whatever you want with s_Name
Next

In C#:

foreach(string s_Name in o_MyCollectionOfNames)
{
    // Do whatever you want with s_Name
}

The code above should be familiar to all of you, even those of you who have not made the transition from Visual Basic 6.0 yet. A tutorial that describes how to use these interfaces and abstract classes is perhaps an excellent topic for another article, but here I will concentrate on just two of these interfaces: IEnumerable and IEnumerator. The class I will iterate through is not a collection class because it does not inherit from either CollectionBase nor DictionaryBase, nor does it implement ICollection or IList. In fact, my class will not even contain a collection; it is just a couple of simple properties that define beginning and ending markers. Intrigued yet? Let’s get going.

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 1a and Listing1b. As many of you know from the companies your 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 and MonthEnd; 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
Next

In C#:

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 class-a 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 2a and Listing 2b. 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 and MonthEnd. 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 classes-well that ends now.

By implementing the IEnumerator interface in the class you add two methods (MoveNext and Reset) 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

In C#:

private int i_Pos;
private DateTime dt_Date;

The i_Pos variable contains your position in the iteration, 0 being the first value (or the equivalent of the MonthBeg value), and the last values is the difference of the begin and end dates. The dt_Date variable will hold the current date during the iteration.

The .NET Framework’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()
   Reset()
End Sub

Public Sub Reset() Implements _
       System.Collections.IEnumerator.Reset
   i_Pos = -1
End Sub

In C#:

public DayEnumerator()
{
    Reset();
}
public void Reset()
{
    i_Pos = -1;
}

The For-Each statement then begins to move through the iteration using the enumerator object’s MoveNext method.

In VB .NET:

Public Function MoveNext() As Boolean Implements _
         System.Collections.IEnumerator.MoveNext
    If i_Pos = -1 Then
        i_Pos += 1
        dt_Date = _FromDate
        Return True
    Else
        If dt_Date < _ToDate Then
            i_Pos += 1
            dt_Date = _FromDate.AddDays(i_Pos)
            Return True
        Else
            Return False
        End If
    End If
End Function

In C#:

public bool MoveNext()
{
    if(i_Pos == -1)
    {
        i_Pos++;
     dt_Date = _FromDate;
     return true;
    }
    else
    {
        if(dt_Date < _ToDate)
     {
         i_Pos++;
      dt_Date = _FromDate.AddDays(i_Pos);
      return true;
     }
     else
      return false;
    }
}

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 1a and Listing 1b).

FiscalMonth must implement the other interface, IEnumerable. This interface implements just one method, GetEnumerator, which returns an object of type IEnumerator. Whoa! Slow down-how 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 _
      System.Collections.IEnumerable.GetEnumerator
    Return CType(o_DayEnumerator, IEnumerator)
End Function

In C#:

public IEnumerator GetEnumerator()
{
    return (IEnumerator)o_DayEnumerator;
}

The o_DayEnumerator 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 = _
        New DayEnumerator

In C#:

private DayEnumerator o_DayEnumerator = 
        new DayEnumerator();

Look at the code in Listing 1 and you will also see that setting the MonthBeg and MonthEnd values in either the property declarations or the constructor will filter them down to the MonthBeg and MonthEnd 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, Current, and MoveNext methods there. You can then use the MonthBeg and MonthEnd properties of the FiscalMonth itself during your iteration functionality.

Something Extra

Here’s a cool feature you can put in your FiscalMonth class, and this feature certainly justifies separating it from the DayEnumerator class; you can create another enumerator class called WeekdayEnumerator. This class would be very similar to the first enumerator with one difference. You can add the functionality to the MoveNext method that checks the dt_Date value after the i_Pos counter was incremented. If dt_Date’s value is a Saturday or Sunday, you can increment the i_Pos counter again, either one or two more times.

Now you have two enumerators that your FiscalMonth class can use. However, you can only have one implementation of the GetEnumerator method, so you need a variable to note which enumerator to use. First you would need to add a declaration of your new enumerator along with the old one.

In VB .NET:

Private o_DayEnumerator As DayEnumerator = _
        New DayEnumerator
Private o_WeekdayEnumerator As _
        WeekdayEnumerator = New DayEnumerator

In C#:

private DayEnumerator o_DayEnumerator = 
        new DayEnumerator();
private WeekdayEnumerator o_WeekdayEnumerator =
        new WeekdayEnumerator();

Then you need a variable to determine which enumerator to use. Ideally you should set up an Enum but for simplicity I will just use a Boolean called WeekdayOnly, with a value of False to indicate the use of the DayEnumerator and a value of True to indicate the use of the WeekdayEnumerator.

Finally, you need to modify the GetEnumerator class to determine which enumerator to use based on the value of this variable.

In VB .NET:

Public Function GetEnumerator() As _
      System.Collections.IEnumerator Implements _
      System.Collections.IEnumerable.GetEnumerator
    If Not WeekdaysOnly Then
        Return CType(o_DayEnumerator, IEnumerator)
    Else
        Return CType( _
               o_WeekdayEnumerator, IEnumerator)
    End If
End Function

In C#:

public IEnumerator GetEnumerator()
{
    if(!WeekdaysOnly)
        return (IEnumerator)o_DayEnumerator;
    else
     return (IEnumerator)o_WeekdayEnumerator;
}

When iterating you would just set this property from your client code before executing a For-Each statement on your FiscalMonth class.

Conclusion

Learning techniques such as this gives you more insight into the inner workings of the .NET Framework; workings that were hidden from us in Visual Basic 6.0 but are exposed and extendible in .NET. This is crucial if you want to take full advantage of all of the .NET Framework’s great features and is a concept I plan to keep in mind for any future articles.

The code accompanying this article contains the full code used here, plus the all-in-one FiscalMonth class with the built-in enumerator, along with the complete WeekdayEnumerator class.

Miguel Castro

&

By: Miguel Castro

Miguel is an architect with IDesign who specializes in architecture consulting and building .NET solutions. He is a Microsoft MVP and INETA speaker and has been a software developer for over 22 years. With a Microsoft background that goes all the way back to VB 1.0 (and QuickBasic in fact), Miguel jumped on .NET as soon as the first public Beta was released and has provided .NET solutions for clients around the country in a variety of industries. He considers himself to be a .NET Developer and Architect and has equal love for both VB and C#, and no tolerance for language bigotry. He’s spoken at numerous user groups around the country as well as developer conferences.

He’s the author of the CodeBreeze code-generator, which among things can be found on his Web site:

www.steelbluesolutions.com

Miguel currently lives in Lincoln Park, NJ with his wife Elena and his daughter Victoria.

subscriptions@infotekcg.com

Fast Facts

Develop custom enumerating functionality and take advantage of iteration through non-collection-based classes.



Listing 1a: FiscalMonth Class - VB .NET
    Public Class FiscalMonth

        Implements IEnumerable

        Public Sub New()
            _WeekdaysOnly = False
        End Sub

        Public Sub New(ByVal arg_FromDate As DateTime, _
                   ByVal arg_ToDate As DateTime)
            _FromDate = arg_FromDate
            _ToDate = arg_ToDate
            o_DayEnumerator.FromDate = arg_FromDate
            o_DayEnumerator.ToDate = arg_ToDate
            o_WeekdayEnumerator.FromDate = arg_FromDate
            o_WeekdayEnumerator.ToDate = arg_ToDate
        End Sub

        Public Sub New(ByVal arg_FromDate As DateTime, _
                   ByVal arg_ToDate As DateTime, _                   ByVal arg_WeekdaysOnly As Boolean)
            Me.New(arg_FromDate, arg_ToDate)
            _WeekdaysOnly = arg_WeekdaysOnly
        End Sub

        Private _FromDate As DateTime
        Private _ToDate As DateTime
        Private _WeekdaysOnly As Boolean
        Private o_DayEnumerator As DayEnumerator = _
                New DayEnumerator
        Private o_WeekdayEnumerator As WeekdayEnumerator = _
                New WeekdayEnumerator

        Public Property FromDate() As DateTime
            Get
                Return _FromDate
            End Get
            Set(ByVal Value As DateTime)
                _FromDate = Value
                o_DayEnumerator.FromDate = Value
            End Set
        End Property

        Public Property ToDate() As DateTime
            Get
                Return _ToDate
            End Get
            Set(ByVal Value As DateTime)
                _ToDate = Value
                o_DayEnumerator.ToDate = Value
            End Set
        End Property

        Public Property WeekdaysOnly() As Boolean
            Get
                Return _WeekdaysOnly
            End Get
            Set(ByVal Value As Boolean)
                _WeekdaysOnly = Value
            End Set
        End Property

        Public Function GetEnumerator() As _
               System.Collections.IEnumerator Implements _
               System.Collections.IEnumerable.GetEnumerator
            If Not _WeekdaysOnly Then
                Return CType(o_DayEnumerator, IEnumerator)
            Else
                Return CType(o_WeekdayEnumerator, IEnumerator)
            End If
        End Function

    End Class


Listing 1b: FiscalMonth Class - C#
   public class FiscalMonth
   {
      public FiscalMonth()
      {
         WeekdaysOnly = false;
      }

      public FiscalMonth(DateTime fromDate, DateTime toDate)
      {
         _FromDate = fromDate;
         _ToDate = toDate;
         o_DayEnumerator.FromDate = fromDate;
         o_DayEnumerator.ToDate = toDate;
         o_WeekdayEnumerator.FromDate = fromDate;
         o_WeekdayEnumerator.ToDate = toDate;
      }

      public FiscalMonth(DateTime fromDate, DateTime toDate,
                   bool weekdaysOnly) : this(fromDate, toDate)
      {
         _WeekdaysOnly = weekdaysOnly;
      }

      private DateTime _FromDate;
      private DateTime _ToDate;
      private bool _WeekdaysOnly;
      private DayEnumerator o_DayEnumerator = 
                    new DayEnumerator();
      private WeekdayEnumerator o_WeekdayEnumerator = 
                    new WeekdayEnumerator();

      public DateTime FromDate 
      {
         get
         {
            return _FromDate;
         }
         set
         {
            _FromDate = value;
            o_DayEnumerator.FromDate = value;
            o_WeekdayEnumerator.FromDate = value;
         }
      }

      public DateTime ToDate
      {
         get
         {
            return _ToDate;
         }
         set
         {
            _ToDate = value;
            o_DayEnumerator.ToDate = value;
            o_WeekdayEnumerator.ToDate = value;
         }
      }

      public bool WeekdaysOnly
      {
         get
         {
            return _WeekdaysOnly;
         }
         set
         {
            _WeekdaysOnly = value;
         }
      }

      public IEnumerator GetEnumerator()
      {
         if(!_WeekdaysOnly)
            return (IEnumerator)o_DayEnumerator;
         else
            return (IEnumerator)o_WeekdayEnumerator;
      }
   }


Listing 2a: DayEnumerator Class - VB.NET
    Public Class DayEnumerator

        Implements IEnumerator

        Private i_Pos As Integer
        Private dt_Date As DateTime
        Private _FromDate As DateTime
        Private _ToDate As DateTime

        Public Sub New()
            Reset()
        End Sub

        Friend Property FromDate() As DateTime
            Get
                Return _FromDate
            End Get
            Set(ByVal Value As DateTime)
                _FromDate = Value
            End Set
        End Property

        Friend Property ToDate() As DateTime
            Get
                Return _ToDate
            End Get
            Set(ByVal Value As DateTime)
                _ToDate = Value
            End Set
        End Property

        Public Function MoveNext() As Boolean Implements _
               System.Collections.IEnumerator.MoveNext
            If i_Pos = -1 Then
                i_Pos += 1
                dt_Date = _FromDate
                Return True
            Else
                If dt_Date _ToDate Then
                    i_Pos += 1
                    dt_Date = _FromDate.AddDays(i_Pos)
                    Return True
                Else
                    Return False
                End If
            End If
        End Function

        Public ReadOnly Property Current() As Object Implements _
               System.Collections.IEnumerator.Current
            Get
                Dim o_Ts As TimeSpan = _ToDate.Subtract(_FromDate)
                If i_Pos OrElse i_Pos o_Ts.Days Then
                    Throw New InvalidOperationException
                Else
                    Return dt_Date
                End If
            End Get
        End Property

        Public Sub Reset() Implements _
               System.Collections.IEnumerator.Reset
            i_Pos = -1
        End Sub

    End Class


Listing 2b: DayEnumerator Class - C#
   public class DayEnumerator : IEnumerator
   {
      private int i_Pos;
      private DateTime dt_Date;
      private DateTime _FromDate;
      private DateTime _ToDate;

      public DayEnumerator()
      {
         Reset();
      }

      internal DateTime FromDate 
      {
         get
         {
            return _FromDate;
         }
         set
         {
            _FromDate = value
         }
      }

      internal DateTime ToDate 
      {
         get
         {
            return _ToDate;
         }
         set
         {
            _ToDate = value;
         }
      }

      public bool MoveNext()
      {
         if(i_Pos == -1)
         {
            i_Pos++;
            dt_Date = _FromDate;
            return true;
         }
         else
         {
            if(dt_Date _ToDate)
            {
               i_Pos++;
               dt_Date = _FromDate.AddDays(i_Pos);
               return true;
            }
            else
               return false;
         }
      }

      public object Current
      {
         get
         {
            TimeSpan o_Ts = 
                          _ToDate.Subtract(_FromDate);
            if(i_Pos 0 || i_Pos o_Ts.Days)
            {
               throw
                               new InvalidOperationException());
            }
            else
               return dt_Date;
         }
      }

      public void Reset()
      {
         i_Pos = -1;
      }
   
   }


How would you rate the quality of this article?
1 2 3 4 5
Poor      Outstanding

Tell us why you rated the content this way. (optional)

Average rating:
2.7 out of 5

3 people have rated this article.

Instantly Search Terabytes Of Text
“Lightning Fast”
– Redmond Mag
“Covers all data
sources” – eWeek
25+ fielded & full-text search options
dtSearch’s own document filters highlight hits in popular file types
Web Spider supports static & dynamic data
APIs for .NET, Java, C++, SQL, etc.
Win / Linux (64-bit & 32-bit)
www.dtSearch.com
 

      AppsWorld Europe

 

Sharepoint TechCon