- 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
Switching to yield Is Much More Efficient
Basically the code in Listing 2 returns a subset of the entire schedule. Generally what we really want is just the ability to iterate over (and perform some operation on) that subset. This is what yield permits us to do.
By using yield, we do not have to create a second List<T>, and we don't have to insert references into the second list. The yield keyword effectively takes care of all of this for us (see Listing 3).
Listing 3 A revised implementation of GetAprilEvents.
public static IEnumerable<Event> GetAprilEvents(List<Event> all) { foreach(Event ev in all) if(ev.Occurs.Month == 4) yield return ev; }
The code looks like it might return after the first if-test is true. What the code in fact does is each time it hits yield return, it sets the Current property of the iterator (IEnumerable's Current property) to the object in the yield return statement, saves the function state on the stack, and returns from the function. This happens without executing any finally block (if present). The next iteration of the outer for loop the function containing yield is called again, restoring its state from the stack, setting IEnumerable.Current equal to the next property, and so on. Effectively, yield creates a dynamic enumerable list.
The function in Listing 3 uses only 21 bytes of MSIL (or about one-fourth the size of the same behavior in Listing 2).
Figure 2 Switching to yield return results in about 21 bytes of code.