C#: IEnumerable and Yield, not as easy as you thought

Beribey
4 min readJun 21, 2020

The concept of IEnumerable is probably quite well known, when we want to browse all the elements in a list, we often use the foreach function as follows.

foreach(Student student in students) {}
Photo by Pankaj Patel on Unsplash

Collection types in C # such as List, ArrayList, Dictionary v, etc. all implement the IEnumerable interface, so we can use foreach to browse.

The concept of Yield is less known by the people (I see very few people know what yield is, not to mention use). Yield is a commonly used keyword with IEnumerable. Using yield will make your code concise, much higher performance. This article will explain and guide how to apply the yield keyword.

Recalling on IEnumerable

An IEnumerable array has the following properties:
A read-only array that can only be read, cannot add or remove elements.
Only browse in one direction, from beginning to end.
Consider the following scenario, if we want to read a list of students from the file, we usually write

public List<Student> ReadStudentsFromFile(string fileName){   string[] lines = File.ReadAllLines(fileName);    // Create an empty list
List<Student> result = new List<Student>();
foreach (var line in lines){ Student student = ParseTextToStudent(line);
result.Add(student); //Add student into list
}
return result; // Return list
}
var students = ReadStudentsFromFile("students.txt");foreach(var student in students) {};

There is nothing wrong with this code. However, we see that creating lists, adding elements to lists, returning lists can be shortened with the yield keyword as follows

// Change the return type to IEnumerable
public IEnumerable <Student> ReadStudentsFromFile (string fileName)
{
string [] lines = File.ReadAllLines (fileName);
foreach (var line in lines)
{
Student student = ParseTextToStudent (line);
yield return student; // YIELD NÈ
}
}

// Used the same
var students = ReadStudentsFromFile ("students.txt");
foreach (var student in students) {};

You will be wondering: Yes, it’s shortened to 2 lines of code, but the code seems more confusing. I thought so in the past. In the following, I will explain the mechanism of the yield, as well as the reason we should use yield in the code.

Differentiate between return and yield return

We all know the most basic thing when writing a method: The return keyword will end the method, return the result, without running any further statements:

public int GetNumber() { return 5; }Console.WriteLine(GetNumber());

So in this case, when we yield 3 times, why?

public IEnumerable<int> GetNumber()
{
yield return 5;
yield return 10;
yield return 15;
}
foreach (int i in GetNumber()) Console.WriteLine(i); //5 10 15

Why so strange, why do we get all 3 results? We can understand the flow of the program as follows:

  1. When calling the GetNumber method, taking the first part, the program runs to the command line 3, produces 5, prints the console.
  2. Browse to the next word, the program runs into command line 4, get 10 results, print to the screen.
  3. Similar to the last element, after printing, the program ends.

Let’s go back and compare the two methods written at the beginning of the program:

public List <Student> ReadStudentsFromFile (string fileName)
{
string [] lines = File.ReadAllLines (fileName);
List <Student> result = new List <Student> (); // Create an empty list

foreach (var line in lines)
{
Student student = ParseTextToStudent (line);
result.Add (student); // Add student to the list
}
return result; // Return the list
}

public IEnumerable <Student> YieldReadStudentsFromFile (string fileName)
{
string [] lines = File.ReadAllLines (fileName);
foreach (var line in lines)
{
Student student = ParseTextToStudent (line);
yield return student;
}
}
  • In the first method, we return the result after running all of the for function, but the result in a new list, the ReadStudentsFromFile function ends.
  • In the second method, the result is immediately after the parse is the first student, with each subsequent loop, the program will continue running into the YieldReadStudentsFromFile method, taking the result out gradually.

After understanding the nature, we can apply yield in the following cases:

  • The method needs to return a read-only list, only read, not added or removed.
  • As in the above case, suppose we have 50 lines, the ParseTextToStudent function takes 1s 1 time. In the old way, when calling the
  • ReadStudentsFromFile function, we had to wait for 50 seconds. With the YieldReadStudentsFromFile function, the ParseTextToStudent function is only run whenever we read the student’s information, which greatly increases performance (If we only take the first 5 students just wait for 5s).
  • In some cases, the list returns infinite elements or taking entire elements very time-consuming, we must use yield to solve.

The previous article only showed you the basic yield concept. When using the yield function, C # will actually compile the rewrite method into a state machine, implement the Next, Current, … methods of IEnumrator. Those who want to learn more can read more here: http://coding.abel.nu/2011/12/return-ienumerable-with-yield-return/

Yield is a rather difficult question, you may be asked when interviewing for a Senior Developer position. Yield is also one of the “5 superhero brothers” that create the virtue of LINQ (the remaining 4 are: Extension method, Delegate, Lambda expression, Generic).

--

--

Beribey

Always be nice to anybody who has access to my toothbrush.