You need to add foreach support to a class, but the normal way of adding an iterator (i.e., implementing IEnumerable on a type and returning a reference to this IEnumerable from a member function) is not flexible enough. Instead of simply iterating from the first element to the last, you also need to iterate from the last to the first, and you need to be able to step over, or skip, a predefined number of elements on each iteration. You want to make all of these types of iterators available to your class.
Solution
The Container<T> class shown in Example 6-1 acts as a container for a private List<T> called internalList. Container is implemented so you can use it in a foreach loop to iterate through the private internalList.
Example 6-1. Creating custom iterators
public class Container<T> : IEnumerable<T>
{
public Container() {}
private List<T> _internalList = new List<T>();
// This iterator iterates over each element from first to last
public IEnumerator<T> GetEnumerator()
{
return _internalList.GetEnumerator();
}
// This iterator iterates over each element from last to first
public IEnumerable<T> GetReverseOrderEnumerator()
{
foreach (T item in ((IEnumerable<T>)_internalList).Reverse())
{
yield return item;
}
}
// This iterator iterates over each element from last to first stepping
// over a predefined number of elements
public IEnumerable<T> GetReverseStepEnumerator(int step)
{
foreach (T item in ((IEnumerable<T>)_internalList).Reverse().EveryNthItem(step))
Iterators need to provide access to the data elements in the collections they are implemented for. You need a good way to work with sets of data.
Solution
Use LINQ to implement iterator logic, as shown in Example 6-2. The highlighted items are just a few parts of LINQ that can help you with the implementation of iterator logic.
Example 6-2. Implementing iterator logic with LINQ
public class SectionalList<T> : IEnumerable<T>
{
private List<T> _items = new List<T>();
public void Add(T item)
{
_items.Add(item);
}
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerable<T> GetFirstHalf()
{
foreach(T item in _items.Take(_items.Count / 2))
{
yield return item;
}
}
public IEnumerable<T> GetSecondHalf()
{
foreach (T item in _items.Skip(_items.Count / 2))
{
yield return item;
}
}
public IEnumerable<T> GetFilteredValues(Func<T, bool> predicate)
{
foreach (T item in _items.TakeWhile(predicate))
{
yield return item;
}
}
public IEnumerable<T> GetReverseFilteredValues(Func<T, bool> predicate)
You have a requirement that if an iterator encounters malformed or out-of-bounds data that the iterations are to stop immediately.
Solution
It is possible to throw an exception from an iterator, which terminates the iterator and the foreach loop, but a controlled stop to an iterator should not be an exceptional condition. To do this, you can use the yield break statement within your iterator:
public class UpperLimitList<T> : IEnumerable<T>
{
private List<T> _items = new List<T>();
private bool noMoreItemsCanBeAdded;
private int upperLimit = -1;
public int UpperLimit
{
get {return (upperLimit);}
set {upperLimit = value;}
}
public void Add(T name)
{
_items.Add(name);
}
public IEnumerator<T> GetEnumerator()
{
for (int index = 0; index < _items.Count; index++)
You have a class that implements an interface with many methods. These methods support only the interface functionality and don’t necessarily relate well to the other code in your class. You would like to keep the interface implementation code separate from the main class code.
Solution
Use partial classes to separate the interface implementation code into a separate file. For example, you have a class called TriValue that takes three decimal values and performs some operations on them, such as getting the average, the sum, and the product. This code is currently in a file called TriValue.cs, which contains:
public partial class TriValue
{
public decimal First { get; set; }
public decimal Second { get; set; }
public decimal Third { get; set; }
public TriValue(decimal first, decimal second, decimal third)