Yield return in C#: Lazy Magic for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Yield return in C#: When Code Works "Lazily" and That's Cool

\n\n

Imagine you're writing a program that needs to process a million lines from a log file. You honestly load everything into a List<string>, pass it to a filtering method, and then — bam! — the program freezes, memory leaks, and the user nervously hits Ctrl+Alt+Del.

\n\n

Sound familiar? If you've ever encountered such a situation, it's time to get acquainted with the "lazy magic" of C# — the yield return operator. This isn't just syntactic sugar, but a powerful tool that can radically change how you work with collections.

\n\n

In this article, we'll break down the correct use of yield return with real-world examples, look under the hood, and figure out when to use it and when it's better to stay away. Let's go!

\n\n

What is yield return and how does it work?

\n\n

In simple terms, yield return is a way to tell the compiler: "Hey, I don't want to return the entire collection at once. Let me hand over elements one by one, as needed." This approach is called lazy evaluation.

\n\n

Here's what a classic example looks like:

\n\n
public IEnumerable<int> GetNumbers()\n{\n    yield return 1;\n    yield return 2;\n    yield return 3;\n}
\n\n

When you call this method, you don't get an array [1, 2, 3]. Instead, you get an iterator that will hand over numbers one at a time when you ask. Each call to MoveNext() advances the method execution to the next yield return.

\n\n

It's like a conveyor belt: elements are produced only when the consumer needs them. No unnecessary copying, no giant lists in memory — just pure, efficient work.

\n\n

Why is yield return not just "returning a list"?

\n\n

Many beginners think: "Why do I need yield return if I can just collect a List and return it?" Let's break it down with a specific example.

\n\n

Imagine you need to get the first 10 even numbers from an infinite sequence. With yield return, this is done elegantly:

\n\n
public IEnumerable<int> GetEvenNumbers()\n{\n    int i = 0;\n    while (true)\n    {\n        yield return i;\n        i += 2;\n    }\n}\n\n// Usage\nvar firstTenEven = GetEvenNumbers().Take(10).ToList();
\n\n

Try doing the same with List<int> — you'll either end up in an infinite loop or store all generated numbers in memory. Yield return gives us the ability to work with potentially infinite sequences without clogging up RAM.

\n\n

Practical example: reading a large file

\n\n

Let's return to our million-line log file. Here's how beginners typically write it:

\n\n
public List<string> ReadLogLines(string path)\n{\n    var lines = new List<string>();\n    using (var reader = new StreamReader(path))\n    {\n        string line;\n        while ((line = reader.ReadLine()) != null)\n        {\n            lines.Add(line);\n        }\n    }\n    return lines;\n}
\n\n

The problem: the entire file is loaded into memory. If the file weighs 500 MB, the list will weigh about the same. And if you only need to filter error lines? You still load everything.

\n\n

Here's the correct use of yield return:

\n\n
public IEnumerable<string> ReadLogLines(string path)\n{\n    using (var reader = new StreamReader(path))\n    {\n        string line;\n        while ((line = reader.ReadLine()) != null)\n        {\n            yield return line;\n        }\n    }\n}\n\n// Filtering without loading the entire file\nvar errorLines = ReadLogLines("log.txt")\n    .Where(line => line.Contains("ERROR"))\n    .Take(100);<                

Blogs

Book Recommendations