C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
55: IAsyncEnumerable and Async Streams
Up until now, we've dealt with Task<List<T>> for asynchronous operations that return a collection. That works fine for a few dozen items, but imagine you're building a dashboard that streams thousands of log entries or a live news feed. If you use a List, the user has to wait for the entire collection to be fetched and loaded into memory before they see a single line of data. It's a terrible user experience.
That's where IAsyncEnumerable<T> comes in. It allows us to "stream" data. Instead of waiting for the whole bucket to fill up, we can hand over each item the moment it's ready. It's essentially the asynchronous version of yield return.
Simulating a Slow News API
Let's build a simple news aggregator. I want a method that simulates fetching articles from a remote server. Since network requests take time, I'll use Task.Delay to mimic that lag. Here is how I'll set up the provider:
public class NewsService
{
private readonly string[] _mockArticles =
{
"C# 12 released!",
"Async streams are powerful",
"The rise of .NET 8",
"Understanding Memory<T>",
"Exploring Source Generators"
};
public async IAsyncEnumerable<string> GetNewsFeedAsync()
{
foreach (var article in _mockArticles)
{
// Simulate network latency for each item
await Task.Delay(500);
yield return article;
}
}
}
Notice the async IAsyncEnumerable<string> return type. This tells the compiler that this method is a state machine that will produce values over time, and that producing those values might involve awaiting other tasks.
The "foreach" trap
Now, here is where I usually trip up when I'm moving quickly. My muscle memory is so tuned to IEnumerable that I'll try to consume the stream like this:
var service = new NewsService();
// This will NOT compile!
foreach (var news in service.GetNewsFeedAsync())
{
Console.WriteLine(news);
}
I get a compiler error immediately. Why? Because IAsyncEnumerable isn't a standard collection; it's a stream of tasks. A regular foreach loop doesn't know how to await the next item in the sequence. It's trying to iterate over the "stream" itself, not the values produced by the stream.
Streaming with await foreach
To fix this, we use await foreach. This tells C# to asynchronously wait for the next element to be yielded before executing the loop body. It keeps the thread free while the Task.Delay is happening in the background.
var service = new NewsService();
Console.WriteLine("Fetching news...");
await foreach (var news in service.GetNewsFeedAsync())
{
Console.WriteLine($"Breaking News: {news}");
}
Console.WriteLine("Feed complete.");
If you run this, you'll see the articles pop up one by one every half-second. If we had used a Task<List<string>>, the console would have stayed blank for 2.5 seconds and then dumped all five lines at once. In a real-world UI, that's the difference between a "frozen" app and a responsive one.
One quick tip: if you're using IAsyncEnumerable in a project, make sure you have using System.Collections.Generic; at the top. It's where the interface lives. Also, remember that you can only use await foreach inside an async method.
📋 Practical Task
Build a Real-Time Log Streamer
Your task is to create a log simulation system that mimics reading a large log file from a disk or a remote server.
- Create a class called
LogReader. - Implement a method
ReadLogsAsync()that returns anIAsyncEnumerable<string>. - Inside the method, use a loop to yield at least 10 mock log messages (e.g., "Log entry 1: System started", "Log entry 2: Connection established", etc.).
- Introduce a random delay between 100ms and 1000ms for each log entry using
Task.Delay()and aRandomobject to simulate unpredictable disk I/O. - In your
Mainmethod, consume this stream usingawait foreachand print each log to the console as it arrives.
There are no comments for now.