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
235: Recursive Patterns
A few years ago, I was mentoring a developer who was tasked with building a category navigation menu for a large e-commerce site. The categories were nested—Electronics had "Computers," which had "Laptops," which had "Gaming Laptops." He started by writing a foreach loop to get the top-level categories. Then he added another foreach inside that for the sub-categories. Then a third. By the time he hit the fourth level of nesting, his code was indented so far to the right that he was scrolling horizontally just to read a single line. He was exhausted, and the code was a nightmare to maintain.
The problem was that he was trying to solve a recursive problem with iterative tools. When you don't know how deep your data goes, loops are your enemy. This is where recursive patterns come in. In C#, recursion is simply a method that calls itself to solve a smaller version of the same problem until it reaches a point where it can stop.
Thinking in Trees
Most of the time, you'll use recursion when dealing with "tree" structures. Think of file directories, organizational charts, or the nested category example I mentioned. Instead of trying to predict the depth of the tree, you write a method that handles one single "node" and then tells itself to do the exact same thing for any children that node might have.
Take a look at this pattern. Imagine we have a simple Category class:
public class Category
{
public string Name { get; set; }
public List<Category> SubCategories { get; set; } = new List<Category>();
}
If we want to print every category in the system, regardless of how deep they are, we don't use nested loops. We use a recursive method like this:
public void PrintCategoryTree(Category category, int indentLevel = 0)
{
// Print the current category with some indentation for visual clarity
Console.WriteLine(new string(' ', indentLevel * 2) + category.Name);
// The recursive step: call the same method for every sub-category
foreach (var sub in category.SubCategories)
{
PrintCategoryTree(sub, indentLevel + 1);
}
}
I love this approach because the logic is incredibly lean. The method doesn't care if the tree is two levels deep or two thousand. It just handles the current item and delegates the rest of the work back to itself.
The Guardrail: The Base Case
Here is where things can go wrong. If you've ever had a program freeze and then crash with a StackOverflowException, you've experienced a recursive function without a proper "base case." Every recursive method needs a condition that tells it when to stop calling itself. If it doesn't, it will keep adding frames to the call stack until the memory allocated for that stack is completely exhausted.
In the PrintCategoryTree example above, the base case is implicit: the foreach loop. If a category has no sub-categories, the loop doesn't execute, the method returns, and the recursion "unwinds."
However, when working with more complex recursion—like searching for a specific file in a directory—you need to be more explicit. You might check if a folder is empty or if you've reached a maximum depth limit to prevent the program from diving into a symbolic link loop that goes on forever. I always tell my team: before you write a single line of recursive code, identify exactly what the "exit" condition is. If you can't define the stop point, don't start the recursion.
📋 Practical Task
Build a Recursive Folder Size Calculator
Your task is to create a program that calculates the total size of a directory, including all its sub-directories and files. This is a classic recursive problem because you never know how many folders are nested within folders.
Requirements:
- Create a method
long GetDirectorySize(string path). - Inside the method, use
Directory.GetFiles(path)to sum the size of all files in the current directory (you can usenew FileInfo(file).Lengthto get the size in bytes). - Use
Directory.GetDirectories(path)to get all sub-folders. - For each sub-folder found, call
GetDirectorySizerecursively and add its result to your total. - Ensure your code handles the case where a directory might be empty (your base case).
Test Case: Create a folder on your machine with a few nested folders and files of varying sizes. Run your calculator and verify that the total matches the properties window of the root folder in Windows Explorer or macOS Finder.
There are no comments for now.