๐ Lambdas & Iterators: Your Magic Helpers in C#
Imagine you have a tiny robot friend who can do ONE thing really well, and you can tell it what to do in just one sentence. Thatโs a Lambda! And imagine a patient librarian who hands you ONE book at a time from a huge pile. Thatโs an Iterator!
๐ญ The Story: Meet Your Two Magic Helpers
Once upon a time, you had a big toy box with hundreds of toys. You needed two helpers:
- Lambda the Quick Helper - Does small tasks instantly when you ask
- Iterator the Patient Helper - Hands you toys one by one, never all at once
Letโs meet them!
๐ Chapter 1: Lambda Expressions
What is a Lambda?
Think of a lambda like giving someone a very short instruction without needing to explain everything.
Normal way (long):
// Like writing a full letter
public int AddFive(int number)
{
return number + 5;
}
Lambda way (short and sweet):
// Like sending a quick text
x => x + 5
The => is called the โarrowโ and it means โgoes toโ or โdoes this.โ
How to Read It
x => x + 5
x= the thing Iโm getting=>= โgoes toโ or โturns intoโx + 5= the result
It reads: โx goes to x plus 5โ
Simple Examples
// Add two numbers
(a, b) => a + b
// Check if number is big
n => n > 10
// Say hello
name => quot;Hello, {name}!"
Using Lambdas with Lists
List<int> numbers = new List<int>
{ 1, 2, 3, 4, 5 };
// Find all numbers bigger than 2
var bigNumbers = numbers
.Where(n => n > 2);
// Result: 3, 4, 5
// Double each number
var doubled = numbers
.Select(n => n * 2);
// Result: 2, 4, 6, 8, 10
๐ Chapter 2: Closures
What is a Closure?
A closure is when a lambda remembers something from outside itself. Like a kid who remembers their parentโs name even when theyโre at school!
graph TD A["๐ Outside Variable"] --> B["๐ฆ Lambda Created"] B --> C["๐ Closure Captures It"] C --> D["๐ Lambda Called Later"] D --> E["โจ Still Remembers!"]
Example: The Counting Toy
int toyCount = 0;
Action addToy = () =>
{
toyCount++; // Remembers toyCount!
Console.WriteLine(quot;Toys: {toyCount}");
};
addToy(); // Toys: 1
addToy(); // Toys: 2
addToy(); // Toys: 3
The lambda captured toyCount and remembers it every time!
Why Closures are Magical
Func<int, int> MakeMultiplier(int by)
{
// 'by' gets captured!
return x => x * by;
}
var double = MakeMultiplier(2);
var triple = MakeMultiplier(3);
Console.WriteLine(double(5)); // 10
Console.WriteLine(triple(5)); // 15
Each lambda remembers its own by value!
๐ Chapter 3: The Yield Keyword
What is Yield?
Imagine youโre reading a HUGE storybook. yield is like reading one page at a time instead of reading the whole book before telling anyone about it.
graph TD A["๐ Big Collection"] --> B["yield return"] B --> C["๐ Give One Item"] C --> D["โธ๏ธ Pause Here"] D --> E["๐ Asked for More?"] E -->|Yes| B E -->|No| F["๐ Stop"]
Without Yield (Get Everything First)
List<int> GetNumbers()
{
List<int> result = new List<int>();
result.Add(1);
result.Add(2);
result.Add(3);
return result; // All at once!
}
With Yield (Give One at a Time)
IEnumerable<int> GetNumbers()
{
yield return 1; // Give 1, pause
yield return 2; // Give 2, pause
yield return 3; // Give 3, done
}
Why Yield is Amazing
IEnumerable<int> CountForever()
{
int number = 1;
while (true)
{
yield return number;
number++;
}
}
// Only takes what we need!
foreach (int n in CountForever().Take(5))
{
Console.WriteLine(n);
}
// Prints: 1, 2, 3, 4, 5
We created INFINITE numbers but only used 5!
๐ Chapter 4: Custom Iterators
What is an Iterator?
An iterator is a helper that knows how to walk through your collection, one step at a time.
Making Your Own Iterator
class ToyBox : IEnumerable<string>
{
string[] toys = { "Ball", "Car", "Doll" };
public IEnumerator<string> GetEnumerator()
{
foreach (string toy in toys)
{
yield return toy;
}
}
// Required for interface
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
}
Now you can use it like this:
var box = new ToyBox();
foreach (string toy in box)
{
Console.WriteLine(toy);
}
// Ball, Car, Doll
Custom Logic in Iterators
IEnumerable<int> EvenNumbers(int max)
{
for (int i = 2; i <= max; i += 2)
{
yield return i;
}
}
foreach (int n in EvenNumbers(10))
{
Console.WriteLine(n);
}
// Prints: 2, 4, 6, 8, 10
๐ Chapter 5: Iterator Patterns
Pattern 1: Filtering
Only give items that match a rule:
IEnumerable<T> Filter<T>(
IEnumerable<T> items,
Func<T, bool> rule)
{
foreach (var item in items)
{
if (rule(item))
yield return item;
}
}
// Usage
var bigNumbers = Filter(
new[] { 1, 5, 10, 15 },
n => n > 7);
// Result: 10, 15
Pattern 2: Transforming
Change each item as you give it:
IEnumerable<R> Transform<T, R>(
IEnumerable<T> items,
Func<T, R> change)
{
foreach (var item in items)
{
yield return change(item);
}
}
// Usage
var doubled = Transform(
new[] { 1, 2, 3 },
n => n * 2);
// Result: 2, 4, 6
Pattern 3: Pagination
Give items in chunks:
IEnumerable<List<T>> GetPages<T>(
IEnumerable<T> items,
int pageSize)
{
List<T> page = new List<T>();
foreach (var item in items)
{
page.Add(item);
if (page.Count == pageSize)
{
yield return page;
page = new List<T>();
}
}
if (page.Count > 0)
yield return page;
}
Pattern 4: Lazy Evaluation Chain
var result = numbers
.Where(n => n > 5) // Iterator 1
.Select(n => n * 2) // Iterator 2
.Take(10); // Iterator 3
// Nothing happens until...
foreach (var n in result) // NOW it runs!
{
Console.WriteLine(n);
}
Each step is lazy - it only works when you ask for values!
๐ฏ Quick Summary
| Concept | What It Does | One-Liner Example |
|---|---|---|
| Lambda | Short function | x => x * 2 |
| Closure | Lambda remembers outside stuff | () => count++ |
| Yield | Give one item, pause | yield return item; |
| Iterator | Walk through collection | IEnumerable<T> |
| Pattern | Reusable iterator recipe | Filter, Transform, Page |
๐ You Did It!
You now understand the magic helpers in C#:
- Lambdas give you quick, one-line functions
- Closures let lambdas remember things
- Yield serves items one at a time
- Iterators let you walk through any collection
- Iterator Patterns solve common problems elegantly
These tools make your code shorter, smarter, and more memory-friendly!
๐ Go build something amazing!
