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
172: The yield Keyword and Iterator Methods
Imagine you're at a candy store. There are two ways you could buy your favorite treats. The first way is to grab a massive basket, walk through every single aisle, pick out every single piece of candy you want, and then carry that heavy basket to the checkout. You don't get to eat a single piece until the entire shopping trip is finished and the bag is packed.
The second way is like using a vending machine. You press a button, one piece of candy drops, and you eat it immediately. Then you press the button again, and another one drops. You never have to carry a heavy basket, and you start enjoying your snacks the second you hit that first button.
In C#, returning a List<T> is like the grocery basket. You build the entire collection in memory first, and only then do you hand it over to the caller. The yield keyword, however, turns your method into a vending machine. It allows you to return one item at a time, pausing the method's execution until the caller asks for the next item.
Stop Packing the Whole Bag
When you use yield return, you aren't returning a collection; you're returning an iterator. The code inside your method doesn't actually run the moment you call the method. It only starts running when you start looping over the result (usually with a foreach loop).
Let's look at a real scenario. Suppose you have a massive log file with millions of lines, and you only want to find the lines that contain the word "CRITICAL". If you put those into a List<string>, you might crash your app by running out of memory before you even process the first error.
public IEnumerable<string> FilterCriticalLogs(IEnumerable<string> logLines)
{
foreach (var line in logLines)
{
if (line.Contains("CRITICAL"))
{
// We "yield" the value and PAUSE here.
// The method stays alive, remembering exactly where it was.
yield return line;
}
}
}
I love this pattern because it's incredibly efficient. If the caller only needs the first three critical errors to show in a UI notification, the method stops running as soon as the third one is found. It doesn't matter if there are ten million more lines in the file—your code never even looks at them.
The Magic of the State Machine
You might be wondering: "How does the method remember where it left off?" This is where C# does some heavy lifting for you. When the compiler sees yield, it doesn't treat your method like a normal function. Instead, it generates a hidden "state machine" class behind the scenes.
When the foreach loop asks for the next item, the state machine jumps back into your method exactly where the last yield return occurred. It's a bit like a bookmark in a book. You read a page, put the bookmark in, and close the book. When you're ready for more, you open it exactly to that bookmark.
Ending the Stream Early
Sometimes you want to stop the vending machine entirely before you've hit the end of your data. For that, we use yield break. This tells the iterator that there are no more items coming, and the foreach loop should terminate immediately.
public IEnumerable<int> GetNumbersUntilNegative(IEnumerable<int> numbers)
{
foreach (var n in numbers)
{
if (n < 0)
{
yield break; // Stop everything. No more numbers will be returned.
}
yield return n;
}
}
Just a heads-up: because of this "deferred execution," you can't use yield return in methods that return a List<T> or an array. It must return IEnumerable<T> (or IEnumerator<T>). If you absolutely need a list at the end, you can always call .ToList() on the result of your iterator method, but keep in mind that doing so effectively "packs the basket," defeating the memory-saving purpose of yield.
📋 Practical Task
Implementing a Memory-Efficient Transaction Filter
You are building a financial auditing tool. You have a list of thousands of Transaction objects, but you only want to process transactions that are flagged as "Suspicious" and are over $10,000. To save memory, you must implement this using an iterator method rather than creating a temporary list.
Your Task:
- Create a
Transactionclass with propertiesAmount(decimal) andIsSuspicious(bool). - Write a method called
GetHighValueSuspiciousTransactionsthat takes anIEnumerable<Transaction>as input and returns anIEnumerable<Transaction>. - Use the
yield returnkeyword to return only the transactions that meet both criteria (Suspicious AND > 10,000). - In your
Mainmethod, create a list of test transactions and use aforeachloop to print the amounts of the filtered results.
There are no comments for now.