Yield in csharp
Understanding yield in C# – Complete Guide with Examples
In C#, yield is a special keyword used to create iterators.
It helps return data one item at a time instead of returning the entire collection immediately.
This feature is extremely useful for:
- Large data processing
- Memory optimization
- Lazy loading
- Streaming data
- File reading
- LINQ operations
- Infinite sequences
In this article, we will explore:
- What
yieldis - Why it is used
- How it works internally
- Syntax
- Real-world use cases
- Advantages and limitations
- Interview questions
- Best practices
What is yield in C#?
yield is a keyword that allows a method to return values
one-by-one during iteration instead of returning all values at once.
Normally, methods execute completely before returning data.
But iterator methods using yield:
- Pause execution
- Return one value
- Resume from the same position later
This process continues until all values are returned.
Simple Definition
yield is used to create iterator methods that generate values lazily, one at a time.
Why Do We Need yield?
Imagine you have millions of records.
If you return all records at once:
public List<int> GetNumbers()
{
List<int> numbers = new List<int>();
for (int i = 1; i <= 1000000; i++)
{
numbers.Add(i);
}
return numbers;
}
- High memory usage
- Slower startup
- Entire collection stored in RAM
Now look at the yield version:
public IEnumerable<int> GetNumbers()
{
for (int i = 1; i <= 1000000; i++)
{
yield return i;
}
}
- Values generated only when needed
- Minimal memory usage
- Faster execution start
- Better performance for large data
How yield Works
When execution reaches:
yield return value;
the method:
- Returns the current value
- Pauses execution
- Saves current position
- Resumes later from the same line
This process repeats during iteration.
Visual Flow of yield
Start Method
↓
yield return 1
↓
Pause Method
Next Iteration
↓
Resume Method
↓
yield return 2
↓
Pause Again
Syntax of yield
C# provides two forms:
1. yield return
Returns one value at a time.
yield return value;
2. yield break
Stops iteration completely.
yield break;
Basic Example of yield
using System;
using System.Collections.Generic;
class Program
{
static IEnumerable<int> GetNumbers()
{
yield return 1;
yield return 2;
yield return 3;
}
static void Main()
{
foreach (var number in GetNumbers())
{
Console.WriteLine(number);
}
}
}
Output
1
2
3
Important Concept – Deferred Execution
Deferred Execution
The method does not execute immediately.
Consider this example:
var data = GetNumbers();
At this point:
- Method is NOT fully executed
- No values are generated yet
Execution starts only when iteration begins:
foreach (var item in data)
{
Console.WriteLine(item);
}
Execution Example
public IEnumerable<int> Demo()
{
Console.WriteLine("Started");
yield return 1;
Console.WriteLine("Middle");
yield return 2;
}
Usage:
var result = Demo();
Console.WriteLine("Before foreach");
foreach (var item in result)
{
Console.WriteLine(item);
}
Output
Before foreach
Started
1
Middle
2
Notice: The method starts only during enumeration.
Real-World Use Cases of yield
1. Reading Large Files
Without yield, loading large files can consume huge memory.
Better Approach
public IEnumerable<string> ReadLines(string path)
{
using var reader = new StreamReader(path);
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
Benefits:
- Reads line-by-line
- Very memory efficient
- Suitable for huge files
2. Filtering Data
public IEnumerable<string> FilterNames(List<string> names)
{
foreach (var name in names)
{
if (name.StartsWith("A"))
{
yield return name;
}
}
}
3. Pagination
public IEnumerable<int> GetData()
{
for (int i = 1; i <= 1000000; i++)
{
yield return i;
}
}
Consumer can fetch only required items.
4. Infinite Sequences
public IEnumerable<int> InfiniteNumbers()
{
int i = 1;
while (true)
{
yield return i++;
}
}
Usage:
foreach (var number in InfiniteNumbers())
{
Console.WriteLine(number);
if (number == 5)
break;
}
Output
1
2
3
4
5
Example Using Loop
public IEnumerable<int> EvenNumbers()
{
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0)
{
yield return i;
}
}
}
Usage:
foreach (var num in EvenNumbers())
{
Console.WriteLine(num);
}
Output
2
4
6
8
10
Understanding yield break
yield break is used to stop iteration immediately.
public IEnumerable<int> Numbers()
{
yield return 1;
yield return 2;
yield break;
yield return 3;
}
Output
1
2
The third value is never returned.
Return Types Allowed with yield
Methods using yield must return:
IEnumerableIEnumerable<T>IEnumeratorIEnumerator<T>
Most commonly:
IEnumerable<T>
Example:
public IEnumerable<int> GetNumbers()
{
yield return 1;
}
What Happens Internally?
The C# compiler automatically converts iterator methods into a hidden state machine.
It internally creates code similar to:
IEnumerator<T>
without requiring developers to manually implement:
MoveNext()Current- State tracking
Without yield
Manually implementing iterators is complicated.
class MyIterator : IEnumerator<int>
{
// Manual state management
}
With yield
Much simpler:
yield return value;
This is why yield is widely used.
Advantages of yield
| Advantage | Description |
|---|---|
| Memory Efficient | No need to store entire collection |
| Lazy Execution | Data generated only when required |
| Cleaner Code | Simplifies iterator implementation |
| Better Performance | Faster processing for large datasets |
| Streaming Support | Ideal for file and network operations |
| Easier Maintenance | Less boilerplate code |
Limitations of yield
| Limitation | Explanation |
|---|---|
| Deferred Execution | Execution timing may confuse beginners |
| Debugging Complexity | State machine generated internally |
| Re-execution | Enumeration runs logic again |
| Not Allowed Everywhere | Cannot use with some constructs like ref and out |
Difference Between return and yield return
| return | yield return |
|---|---|
| Returns complete result immediately | Returns one item at a time |
| Method ends completely | Method pauses |
| Eager execution | Lazy execution |
| More memory usage | Memory efficient |
| Suitable for small collections | Suitable for large/streaming data |
LINQ and yield
Many LINQ methods internally rely on iterator behavior.
Examples:
Where()
Select()
Take()
Skip()
These methods use lazy execution principles similar to yield.
Async Streams with yield
Modern C# supports asynchronous iterators using:
IAsyncEnumerable<T>
Example:
public async IAsyncEnumerable<int> GetDataAsync()
{
for (int i = 1; i <= 5; i++)
{
await Task.Delay(1000);
yield return i;
}
}
Useful for:
- Streaming APIs
- Real-time data
- Asynchronous processing
Best Scenarios to Use yield
Use yield when working with:
- Large datasets
- File streaming
- Database record streaming
- LINQ-style processing
- Pagination
- Data pipelines
- Infinite generators
- Lazy loading systems
Common Interview Questions
What is yield in C#?
yield is used to create iterator methods that return values one-by-one using lazy execution.
Difference between return and yield return?
return sends all results immediately, while yield return returns items one at a time.
What is deferred execution?
Execution starts only when enumeration begins.
Which interfaces are used with yield?
IEnumerableIEnumerable<T>IEnumeratorIEnumerator<T>
What does yield break do?
Stops iteration immediately.
Conclusion
The yield keyword is one of the most powerful features in C# for building efficient iterators.
It helps developers:
- Reduce memory usage
- Improve performance
- Simplify iterator code
- Support lazy execution
- Stream data efficiently
Instead of generating all results upfront,
yield produces values only when needed,
making it ideal for modern high-performance applications.
If you work with:
- Large collections
- Streaming data
- LINQ
- File processing
- APIs
- Real-time systems
then understanding yield is extremely important for writing optimized and scalable C# applications.
Comments
Post a Comment