C# tutorials > Core C# Fundamentals > Data Structures and Collections > How do you use `yield return` for custom iterators?
How do you use `yield return` for custom iterators?
The yield return
statement in C# is a powerful feature that enables you to create custom iterators for collections or sequences of data without the need to implement the entire IEnumerable
and IEnumerator
interfaces manually. It allows you to produce a sequence of values one at a time, on demand, which can be very efficient for large datasets or complex calculations.
Basic Implementation
This example demonstrates a simple custom iterator. The GetEvenNumbers
method returns an IEnumerable<int>
. Instead of creating a list and returning it, we use yield return
to return each even number as it's generated. The yield return
statement pauses the execution of the method and returns the value. The next time an element is requested, execution resumes from where it left off.
using System;
using System.Collections.Generic;
public class NumberSequence
{
public IEnumerable<int> GetEvenNumbers(int max)
{
for (int i = 0; i <= max; i++)
{
if (i % 2 == 0)
{
yield return i;
}
}
}
}
public class Example
{
public static void Main(string[] args)
{
NumberSequence sequence = new NumberSequence();
foreach (int number in sequence.GetEvenNumbers(10))
{
Console.WriteLine(number);
}
}
}
Concepts Behind the Snippet
The yield return
statement is the core of custom iterators in C#. When the compiler encounters a yield return
statement in a method, it transforms that method into a state machine. This state machine keeps track of the current state of the iteration, so the method can be resumed from the exact point it was paused. The IEnumerable
interface provides the GetEnumerator()
method, which returns an IEnumerator
object. When you use yield return
, the compiler automatically generates an IEnumerator
implementation for you.
Real-Life Use Case Section
Imagine you're reading a very large file, line by line. Instead of loading the entire file into memory, you can use yield return
to read and process each line as it's needed. This avoids memory issues and improves performance, especially for very large files.
using System;
using System.Collections.Generic;
using System.IO;
public class FileProcessor
{
public IEnumerable<string> ReadLines(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
}
public class Example
{
public static void Main(string[] args)
{
// Assuming you have a file named 'data.txt'
FileProcessor processor = new FileProcessor();
foreach (string line in processor.ReadLines("data.txt"))
{
Console.WriteLine(line);
}
}
}
Best Practices
yield break
to end the iteration early: The yield break
statement exits the iterator block, signaling the end of the sequence.
Interview Tip
Be prepared to explain how yield return
works under the hood. Understanding that the compiler creates a state machine is key. Also, be ready to discuss the benefits of using yield return
, such as memory efficiency and deferred execution.
When to Use Them
Use yield return
when:
Memory Footprint
yield return
significantly reduces the memory footprint compared to creating and returning a complete collection. It only holds the current value and the state of the iterator in memory, rather than the entire sequence.
Alternatives
Alternatives to yield return
include:List<T>
: This loads all the values into memory at once.IEnumerable
and IEnumerator
manually: This gives you more control but requires more code.
Pros
Cons
FAQ
-
What happens when I call a method that uses `yield return`?
When you call a method that uses
yield return
, the method doesn't execute immediately. Instead, the compiler generates an iterator object that implements theIEnumerable
interface. The code within the method is executed only when you start iterating over the iterator object, such as with aforeach
loop. -
Can I use `yield return` in a `try-catch` block?
Yes, you can use
yield return
in atry-catch
block. However, make sure to handle exceptions properly to avoid unexpected behavior. You can also use afinally
block to ensure that resources are released, even if an exception occurs. -
What is the difference between `yield return` and `yield break`?
yield return
is used to return a value from the iterator and pause execution, whileyield break
is used to terminate the iteration and exit the iterator block.