Recipe6.7.Implementing Iterators as Overloaded Operators


Recipe 6.7. Implementing Iterators as Overloaded Operators

Problem

You need the ability to iterate over two or more collections and enumerate all elements in each set using a foreach loop construct. In addition to this, you also want to be able to iterate over the unique elements in each set and the duplicate elements in each set.

Solution

Create overloaded operators that act as iterators. The following code shows how to set up an iterator on an overloaded + operator:

 public static IEnumerable<T> operator +(IEnumerable<T> lhs, IEnumerable<T> rhs) {     foreach (T t in lhs)     {         yield return (t);     }     foreach (T t in rhs)     {         yield return (t);     } } 

We will use the previous code example in creating a Set class, shown in Example 6-5, which makes use of the familiar GetEnumerator iterator method, but also overloads the +, |, and & operators for use as iterators.

Example 6-5. Implementing iterators as overloaded operators (+, |, and &)

  public class Set<T> {     private List<T> _items = new List<T>();     public void AddItem(T name)     {         if (!_items.Contains(name))             _items.Add(name);         else             throw (new ArgumentException("This value can only be added to a set once.",  "name"));     }     public int Count     {         get {return (_items.Count);}     }     public T this[int index]     {         get {return (_items[index]);}         set {_items[index] = value;}     }     public void AddRange(Set<T> original)     {         foreach(T t in original)         {             AddItem(t);         }     }     public bool Contains(T t)     {         if (_items.Contains(t))             return (true);         else             return (false);     }     // Iterators      public IEnumerator<T> GetEnumerator()     {         for (int index = 0; index < _items.Count; index++)         {             yield return (_items[index]);         }     }     public static IEnumerable<T> operator +(Set<T> lhs, Set<T> rhs)     {         for (int index = 0; index < lhs.Count; index++)         {             yield return (lhs[index]);         }         for (int index = 0; index < rhs.Count; index++)         {             yield return (rhs[index]);         }     }     public static IEnumerable<T> operator +(IEnumerable<T> lhs, Set<T> rhs)     {         foreach (T t in lhs)         {             yield return (t);         }         for (int index = 0; index < rhs.Count; index++)         {             yield return (rhs[index]);         }     }     public static IEnumerable<T> operator +(Set<T> lhs, IEnumerable<T> rhs)     {         foreach (T t in rhs)         {             yield return (t);         }                  for (int index = 0; index < lhs.Count; index++)         {             yield return (lhs[index]);         }     }     public static IEnumerable<T> operator |(Set<T> lhs, Set<T> rhs)     {         // Strip out duplicates from lhs Set object.         Set<T> tempSet = new Set<T>();         for (int index = 0; index < lhs.Count; index++)         {             if (!tempSet.Contains(lhs[index]))             {                 tempSet.AddItem(lhs[index]);             }         }         for (int index = 0; index < tempSet.Count; index++)         {             yield return (tempSet[index]);         }         for (int index = 0; index < rhs.Count; index++)         {             if (!tempSet.Contains(rhs[index]))             {                 yield return (rhs[index]);             }         }     }     public static IEnumerable<T> operator |(IEnumerable<T> lhs, Set<T> rhs)     {         // Strip out duplicates from lhs Set object.         Set<T> tempSet = new Set<T>();         foreach (T t in lhs)         {             if (!tempSet.Contains(t))             {                 tempSet.AddItem(t);             }         }         for (int index = 0; index < tempSet.Count; index++)         {             yield return (tempSet[index]);         }         for (int index = 0; index < rhs.Count; index++)         {             if (!tempSet.Contains(rhs[index]))             {                 yield return (rhs[index]);             }         }     }     public static IEnumerable<T> operator |(Set<T> lhs, IEnumerable<T> rhs)     {         // Strip out duplicates from lhs Set object.         Set<T> tempSet = new Set<T>();         foreach (T t in lhs)         {             if (!tempSet.Contains(t))             {                 tempSet.AddItem(t);             }         }         for (int index = 0; index < tempSet.Count; index++)         {             yield return (tempSet[index]);         }         foreach (T t in rhs)         {             if (!tempSet.Contains(t))             {                 yield return (t);             }         }     }     public static IEnumerable<T> operator &(Set<T> lhs, Set<T> rhs)     {         // Strip out duplicates from lhs Set object.         Set<T> tempSet = new Set<T>();         for (int index = 0; index < lhs.Count; index++)         {             if (!tempSet.Contains(lhs[index]))             {                 tempSet.AddItem(lhs[index]);             }         }         for (int index = 0; index < tempSet.Count; index++)         {             if (rhs.Contains(tempSet[index]))             {                 yield return (tempSet[index]);             }         }     }     public static IEnumerable<T> operator &(IEnumerable<T> lhs, Set<T> rhs)     {         // Strip out duplicates from lhs Set object.         Set<T> tempSet = new Set<T>();         foreach (T t in lhs)         {             if (!tempSet.Contains(t))             {                 tempSet.AddItem(t);             }         }         for (int index = 0; index < tempSet.Count; index++)         {             if (rhs.Contains(tempSet[index]))             {                 yield return (tempSet[index]);             }         }     }     public static IEnumerable<T> operator &(Set<T> lhs, IEnumerable<T> rhs)     {         // Strip out duplicates from lhs Set object.         Set<T> tempSet = new Set<T>();         for (int index = 0; index < lhs.Count; index++)         {             if (!tempSet.Contains(lhs[index]))             {                 tempSet.AddItem(lhs[index]);             }         }         foreach (T t in rhs)         {             if (tempSet.Contains(t))             {                 yield return (t);              }          }      }  } 

The following code makes use of the overloaded operator iterators in Example 6-5:

 public static void TestOperatorIterator() {     //Create a Set<string> object and fill it with data.     Set<string> set1 = new Set<string>();     set1.AddItem("item1");     set1.AddItem("item11");     set1.AddItem("item2");     set1.AddItem("item2");     set1.AddItem("item3");     set1.AddItem("XYZ");     //Create a second Set<string> object and fill it with data.     Set<string> set2 = new Set<string>();     set2.AddItem("item30");     set2.AddItem("item11");     set2.AddItem("item11");     set2.AddItem("item2");     set2.AddItem("item12");     set2.AddItem("item1");     // Display all data in both set objects.     Console.WriteLine("\r\nDisplay all data in both sets");     foreach (string s in (set1 + set2))     {         Console.WriteLine(s);     }     // Display all unique data in both set objects.     Console.WriteLine("\r\nDisplay only unique data in both sets");     foreach (string s in (set1 | set2))     {         Console.WriteLine(s);     }     // Display all duplicate data in both set objects.      Console.WriteLine("\r\nDisplay only duplicate data in both sets");     foreach (string s in (set1 & set2))     {         Console.WriteLine(s);     } } 

This code produces the following output:

 Display all data in both sets item1 item11 item2 item2 item3 XYZ item30 item11 item11 item2 item12 item1 Display only unique data in both sets item1 item11 item2 item3 XYZ item30 item12 Display only duplicate data in both sets item1 item11 item2 

Discussion

In addition to allowing methods and property get accessors to be iterator methods, operator overloads can also be made into iterator methods. To become an iterator method, the operator overload method simply has to return a System.Collections.IEnumerable or a System.Collections.Generic.IEnumerable<T> type and implement the yield return statement.

The + operator is overloaded to yield every element of each Set object, both the lhs and rhs Set objects. From the foreach loop's perspective, this effectively allows the loop to iterate over every Set object that is added together as if it were one big Set object. The foreach loop is able to make use of this overloaded operator since the return value of this method is an IEnumerable<T> type. Therefore, a simple foreach loop can be set up as follows to return all elements of two Set objects:

 foreach (string s in (set1 + set2)) 

The | operator is overloaded to return only unique items from both Set objects in the expression. It does this by first creating a temporary Set object (tempSet), which contains all values of the lhs Set object after the duplicate values have been stripped out of it. Once this is completed, all of the tempSet object elements are yielded. Finally, the rhs Set object is examined for any elements that are not already contained within the tempSet object. All of the unique values from the rhs are also yielded.

The & operator is overloaded to return only the items from the lhs Set object that are duplicated in the rhs Set object. It does this by first creatinga temporary Set object (tempSet), which contains all values of the lhs Set object after the duplicate values have been stripped out of it. Once this is completed, the rhs Set object is examined for any duplicate elements that are contained within the tempSet Set object. If a duplicate is found, that element is yielded.

This code works fine if we are constructing foreach loops that contain only one of these operators in the expression. For example, this works fine:

 foreach (string s in (set1 + set2)) 

However, the following code will cause problems:

 foreach (string s in (set1 + set2 + set3)) 

The reason for this is that the expression is evaluated as ((set1 + set2) + set3). Therefore the result of the inner equation is an IEnumerable<T>, which is then added to set3. In order for this equation to work, we also need to overload the + operator to accept a Set<T> object and an IEnumerable<T> object as shown here:

 public static IEnumerable<T> operator +(Set<T> lhs, IEnumerable<T> rhs) (…)  public static IEnumerable<T> operator +(IEnumerable<T> lhs, Set<T> rhs) (…) 

These two extra overloads will allow more complex equations to be evaluated properly for foreach statements.

See Also

See the "Iterators," "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