Using the Yield Keyword for Elegant Code
The yield C# keyword was added in C# 2.0 in order to simplify implementation of the “Iterator Pattern” in Typed C# objects.
The “Gang of Four” in their Design Patterns book in the 90’s defined the Iterator Pattern as:
“Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation”
The Iterator Pattern Demonstration
The Software Entities in the Class Diagram has the following responsibilities:
-
IIterator – The Interface that contracts the Methods for Accessing and Traversing Elements
-
IteratorDemo – The Concrete Iterator that implements the IIterator Interface. The Iterator tracks the current index position in the traversal of the aggregate object
-
IIteration – The interface that defines method: GetIterator. This implementation creates the Iterator object: IterationDemo
-
IterationDemo – The Concrete object that holds the collection. This class implements the IIteration interface for the GetIterator Method
-
InteratorPatternDemo – The Class that is the implementation of the Iterator Pattern for our Unit Test. Notice I have embedded a “Yield Return” to create the IEnumerable of String. This is actually a C# implementation of the Iteration Pattern within the Iteration Pattern demo
+The Iterator Source Code: Interface and Concrete Class
The Interface Contract for the Iterator Concrete Class:
|
1 2 3 4 5 6 7 8 9 10 |
namespace TCMS.POCSandbox.IteratorPattern { public interface IIterator { string FirstItem { get; } string NextItem { get; } string CurrentItem { get; } bool IsDone { get; } } } |
The Iterator Concrete Class:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
namespace TCMS.POCSandbox.IteratorPattern { public class IteratorDemo : IIterator { private IIteration _iteration { get; set; } private int _currentIndex { get; set; } public IteratorDemo(IIteration iteration) { _iteration = iteration; _currentIndex = 0; } public string FirstItem { get { _currentIndex = 0; return _iteration[_currentIndex]; } } public string NextItem { get { _currentIndex += 1; return IsDone == false ? _iteration[_currentIndex] : string.Empty; } } public string CurrentItem { get { return _iteration[_currentIndex]; } } public bool IsDone { get{return _currentIndex >= _iteration.Count;} } } } |
+The Iteration Source Code: Interface and Concrete Class
The Interface Contract for the Iteration Concrete Class:
|
1 2 3 4 5 6 7 8 9 |
namespace TCMS.POCSandbox.IteratorPattern { public interface IIteration { IIterator GetIterator(); string this[int itemIndex] { set; get; } int Count { get; } } } |
The Iteration Concrete Class:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
using System.Collections.Generic; namespace TCMS.POCSandbox.IteratorPattern { public class IterationDemo : IIteration { private List _values { get; set; } public string this[int itemIndex] { get { return itemIndex < _values.Count ? _values[itemIndex] : string.Empty; } set { _values.Add(value); } } public int Count { get { return _values.Count; } } public IterationDemo() { _values = new List(); } public IIterator GetIterator() { return new IteratorDemo(this); } } } |
+The Iterator Pattern Usage Source Code: Collection Creation
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using System.Collections.Generic; namespace TCMS.POCSandbox.IteratorPattern { public class IteratorPatternDemo { public static IEnumerable GetIteratorPatternListOfStrings() { IterationDemo collection = new IterationDemo(); for (int i = 0; i < 10; i++) collection[i] = (i+1).ToString(); IIterator iterator = collection.GetIterator(); for (var item = iterator.FirstItem; iterator.IsDone == false; item = iterator.NextItem) { yield return item; } } } } |
In the Iterator Pattern Unit Test Class we define the validation test method below using the Gherkin Behavior Driven Development (BDD) language abstraction syntax of :
Given | When | Then.
Using Gherkin Syntax defines, as a naming convention abstraction, the expectation of the concrete test:
GivenIWantToTestTheIteratorPatternDemoClass
WhenICallTheGetIteratorPatternListOfStringsMethod
ThenIWillValidateTheMethodResult
The Gherkin Naming Convention defines the “What” of the test for the Developer as a road map for the concrete “How“.
The Test Validates Two Actions:
-
A Greedy result – The Yield Return IEnumerable result as a List<string>
-
A Lazy Result – The Yield Return IEnumerable result as a IEnumerable<string>
+The Unit Test Source Code
This test calls the “GetIteratorPatternListOfStrings()” twice and demonstrates Greedy and Lazy Loading of the collection of 1 to 10 as number strings.
It then asserts that the collections have been created correctly for both.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public static class IteratorPatternDemoTests { [TestClass] public class GivenIWantToTestTheIteratorPatternDemoClass { [TestMethod] public void WhenICallTheGetIteratorPatternListOfStringsMethod_ThenIWillValidateTheMethodResult() { //Arrange //Act List greedyResult = IteratorPatternDemo.GetIteratorPatternListOfStrings().ToList(); IEnumerable lazyResult = IteratorPatternDemo.GetIteratorPatternListOfStrings(); //Assert Assert.IsNotNull(greedyResult); Assert.IsTrue(greedyResult.Count == 10); CollectionAssert.AllItemsAreNotNull(greedyResult); CollectionAssert.AllItemsAreUnique(greedyResult); Assert.IsNotNull(lazyResult); Assert.IsTrue(lazyResult.Count() == 10); CollectionAssert.AllItemsAreNotNull(lazyResult.ToList()); CollectionAssert.AllItemsAreUnique(lazyResult.ToList()); } } } |
The images below show the performance improvement of delaying the actual creation of the results until they are actually required.
Greedy Loading using the ToList() Extension Method:
Lazy Loading using the “yield return‘ Iterator Pattern:
The images above show the performance improvement of delaying the actual creation of the results until they are actually required.
The Yield Return Results are Added to the IEnumerable Collection
… But are Not Actually Returned until Queried
…… Using the Results View Expander in Intellisense
The Power of Yield Return
Using “yield return” simplifies your code while creating a more efficient Iterator Pattern “Under the Hood“.
When ever you see code patterns like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
public List CreateAListOfStrings(IEnumerable list) { List listOfStrings = new List(); foreach (var item in list) { if (item.Contains("SomeCharSet")) { listOfStrings.Add(item); } } return listOfStrings; } |
Replace it with a code pattern using the yield return to loop within the yield and act only when the predicate is true:
|
1 2 3 4 5 6 7 8 9 10 |
public IEnumerable CreateAnIEnumerableOfStrings(IEnumerable list) { foreach (var item in list) { if (item.Contains("SomeCharSet")) { yield return item; } } } |
You will use a smaller memory footprint with better performance without returning any processed collection.
The “yield return” adds one item to the returned IEnumerable<T> each time it is called.
It does not end the function call like a normal return would. The function ends only when the flow of control reaches the end of the function body.
How is this actually accomplished:
The MSDN Docs Explanation:
“The compiler generates a class to implement the behavior that is expressed in the iterator block”
The “yield return” abstracts away the complexity of the Iterator Pattern demo code, detailed above, for you.
You will be creating more elegant code and delivering a better product to your Client.
Using ‘Yield Return’ for IEnumerable<T>
… Simplifies Code with Improved Performance
The following two tabs change content below.
I am a Principal Architect at Liquid Hub in the Philadelphia area specializing in Agile Practices as a Certified Scrum Master (CSM). I use Test Driven Development (TDD) and Acceptance Test Driven Development (ATDD) with Behavior Driven Development (BDD) as my bridge to Agile User Stories Acceptance Criteria in a Domain Driven Design (DDD) implementing true RESTful services
Latest posts by Brad Huett (see all)
- DevOps: A Bridge to Your DevOps Culture - March 25, 2016
- Embracing Test Driven Development (TDD) - March 25, 2016
- DevOps: Delivering Agile Projects - March 25, 2016



