Recipe6.1.Implementing Nested foreach Functionality in a Class


Recipe 6.1. Implementing Nested foreach Functionality in a Class

Problem

You need a class that contains a list of objects; each of these objects in turn contains a list of objects. You want to use a nested foreach loop to iterate through all objects in both the outer and inner arrays in the following manner:

 foreach (SubGroup sg in group) {     foreach (Item i in sg)     {         // Operate on Item objects contained in the innermost object collection sg,          // which in turn is contained in another outer collection called group.     }  } 

Solution

Implement the IEnumerable interface on the class. The Group class shown in Example 6-1 contains a List of SubGroup objects; each SubGroup object contains a List of Item objects. Implement IEnumerable on the top-level class (Group) and on each of the objects returned (SubGroup) by this top-level enumeration.

Example 6-1. Implementing for each functionality in a class

 using System; using System.Collections; //---------------------------------------- //  The top-level class  //---------------------------------------- public class Group : IEnumerable {      //CONSTRUCTORS     public Group() {}     //FIELDS     private List<SubGroup> innerArray = new List<SubGroup>();     //PROPERTIES     public int Count     {         get{return(innerArray.Count);}     }     //METHODS     public void AddGroup(string name)     {         SubGroup subGroup = new SubGroup(name);         innerArray.Add(subGroup);     }     public SubGroup GetGroup(int setIndex)     {         return(innerArray[setIndex]);     }          IEnumerator IEnumerable.GetEnumerator()     {         for (int index = 0; index < Count; index++)         {             yield return (innerArray[index]);          }      }  } //---------------------------------------- // The inner class  //---------------------------------------- public class SubGroup : IEnumerable  {     //CONSTRUCTORS     public SubGroup() {}     public SubGroup(string name)     {         subGroupName = name;     }     //FIELDS     private string subGroupName = "";     private List<Item> itemArray = new List<Item>();     //PROPERTIES     public string SubGroupName     {         get{return(subGroupName);}     }     public int Count     {         get{return(itemArray.Count);}     }     //METHODS     public void AddItem(string name, int location)     {         Item itm = new Item(name, location);         itemArray.Add(itm);     }     public Item GetSubGroup(int index)     {         return(itemArray[index]);     }     IEnumerator IEnumerable.GetEnumerator()     {         for (int index = 0; index < Count; index++)         {             yield return (itemArray[index]);          }      }  } //---------------------------------------- // The lowest-level class  //---------------------------------------- public class Item  {     //CONSTRUCTOR     public Item(string name, int location)     {         itemName = name;         itemLocation = location;     }     private string itemName = "";     private int itemLocation = 0;     public string ItemName     {         get {return(itemName);}         set {itemName = value;}     }     public int ItemLocation     {         get {return(itemLocation);}         set {itemLocation = value;}     }  } 

Discussion

Building functionality into a class to allow it to be iterated over using the foreach loop is much easier now that iterators are available in Version 2.0 of the C# language. In previous versions of the .NET Framework, you not only had to implement the IEnumerable interface on the type that you wanted to make enumerable, but you also had to implement the IEnumerator interface on a nested class. The methods MoveNext and Reset along with the property Current then had to be written by hand in this nested class. Iterators allow you to hand the work of writing this nested class off to the C# compiler.

The ability for a class to be used by the foreach loop requires the inclusion of an iterator. An iterator can be a method, an operator overload, or the get accessor of a property that returns either a System.Collections.IEnumerator, a System.Collections. Generic.IEnumerator<T>, a System.Collections.IEnumerable, or a System.Collections. Generic.IEnumerable<T> and that contains at least one yield statement.

Here are two examples of iterator members implemented using the GetEnumerator method:

 IEnumerator IEnumerable.GetEnumerator() {     for (int index = 0; index < Count; index++)     {         yield return (someArray[index]);     } } IEnumerator<T> IEnumerable<T>.GetEnumerator() {     for (int index = 0; index < Count; index++)     {         yield return (someArray[index]);     } } 

The code for this recipe is divided among three classes. The top-level class is the Group class, which contains a List of SubGroup objects. The SubGroup object also contains a List, but this List contains Item objects. To enumerate their contained lists, both the Group and SubGroup implement the IEnumerable interface. They therefore contain a GetEnumerator iterator method, which returns an IEnumerator. The class structure looks like this:

  Group (Implements IEnumerable)      SubGroup (Implements IEnumerable)          Item 

By examining the Group class, you can see how classes usable by a foreach loop are constructed. This class contains:

  • A simple List, which will be iterated over by the class's enumerator.

  • A property, Count, that returns the number of elements in the List.

  • An iterator method, GetEnumerator, which is defined by the IEnumerable interface. This method yields a specific value on each iteration of the foreach loop.

  • A method, AddGroup, which adds a SubGroup object to the List.

  • A method, GetGroup, which returns a SubGroup object in the List.

To create the SubGroup class, you follow the same pattern as with the Group classexcept the SubGroup class contains a List of Item objects.

The final class is the Item class. This class is the lowest level of this structure and contains data. It has been grouped within the SubGroup objects, all of which are contained in the Group object. There is nothing out of the ordinary with this class; it simply contains data and the means to set and retrieve this data.

Using these classes is quite simple. The following method shows how to create a Group object that contains multiple SubGroup objects, which in turn contain multiple Item objects:

 public void CreateNestedObjects() {     Group topLevelGroup = new Group();     // Create two groups under the TopLevelSet object.     topLevelGroup.AddGroup("sg1");     topLevelGroup.AddGroup("sg2");     // For each SubGroup object in the topLevelGroup object add two Item objects.     foreach (SubGroup SG in topLevelGroup)     {         SG.AddItem("item1", 100);         SG.AddItem("item2", 200);     } } 

The CreateNestedObjects method first creates a topLevelGroup object of the Group class, then creates two SubGroups within it called sg1 and sg2. Each of these SubGroup objects in turn is filled with two Item objects called item1 and item2.

The next method shows how to read all of the Item objects contained within the Group object that was created in the CreateNestedObjects method:

 public void ReadNestedObjects(Set topLevelGroup)  {      Console.WriteLine("topLevelGroup.Count: " + topLevelGroup.Count);          // Outer foreach to iterate over all SubGroup objects     // in the topLevelGroup object     foreach (SubGroup SG in topLevelGroup)     {         Console.WriteLine("\tSG.SubGroupName: " + SG.SubGroupName);          Console.WriteLine("\tSG.Count: " + SG.Count);         // Inner foreach to iterate over all Item objects          // in the current SubGroup object          foreach (Item i in SG)         {             Console.WriteLine("\t\ti.ItemName:     " + i.ItemName);             Console.WriteLine("\t\ti.ItemLocation: " + i.ItemLocation);         }     } } 

This method displays the following:

 topLevelGroup.Count: 2         SG.SubGroupName: sg1         SG.Count: 2                 I.ItemName: item1                 I.ItemLocation: 100                 I.ItemName: item2                 I.ItemLocation: 200         SG.SubGroupName: sg2         SG.Count: 2                 I.ItemName: item1                 I.ItemLocation: 100                 I.ItemName: item2                 I.ItemLocation: 200 

As you see here, the outer foreach loop is used to iterate over all SubGroup objects that are stored in the top-level Group object. The inner foreach loop is used to iterate over all Item objects that are stored in the current SubGroup object.

See Also

See the "Iterators," "yield," "IEnumerator Interface," and "IEnumerable Interface" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net