- Iterating Collections and Creating Sub-lists
- Switching to yield Is Much More Efficient
- Mixing in a Predicate<T> Affords Maximum Flexibility
- Knowing yield's Basic Rules and Limitations
- Summary
Mixing in a Predicate<T> Affords Maximum Flexibility
Listing 3 is pretty short and easy to write, but it isn't very flexible. If we change the test behavior to use a Predicate<T>, then one iterator can be used for any sublist. The only part you have to write is the Predicate<T> delegate (the test method itself.)
Listing 4 contains a very general method GetEvents and separates the predicate (test) into a separate delegate method.
Listing 4 Using a Predicate<T> delegate to separate iteration from test behavior.
using System; using System.Collections.Generic; using System.Text; namespace YieldReturn { class Program { static void Main(string[] args) { List<Event> events = new List<Event>(); events.Add(new Event(new DateTime(2007, 4, 6), "Rent")); events.Add(new Event(new DateTime(2007, 6, 15), "Chicago")); events.Add(new Event(new DateTime(2007, 6, 15), "The Fray")); events.Add(new Event(new DateTime(2007, 4, 1), "Wrestlemania")); foreach(Event ev in GetEvents(events, MatchApril)) Console.WriteLine(ev); Console.ReadLine(); } private static bool MatchApril(Event ev) { return ev.Occurs.Month == 4; } public static IEnumerable<Event> GetEvents(List<Event> all, Predicate<Event> match) { foreach(Event ev in all) if(match(ev)) yield return ev; } } }
The final revision is a little bigger then the code in Listing 3, but it is very general and very flexible. All you have to do is write versions the Predicate<T> for each kind of test. (Predicate<T> is satisfied by the MatchApril method.)
The subtle addition of iterators and predicates support methods such as the FindAll<T> that is implemented with List<T>. So, in fact, you can skip writing GetEvents and use List<T>.FindAll. Then, all you have to write is the predicate behavior.