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
193: ToList, ToArray, ToDictionary
A few years ago, I was reviewing a pull request from a junior dev who was building a reporting module. He had a LINQ query that filtered a massive list of transactions for "Pending" status and passed that IEnumerable around to three different methods. The weird part? Each time a method iterated over that list, the results were slightly different because the underlying data source was being updated by another thread. He spent half a day chasing a "ghost bug" before I pointed out that he wasn't actually storing a list of transactions—he was storing the instructions on how to find them. He hadn't materialized the query.
The Deferred Execution Trap
In C#, when you use LINQ methods like Where or Select, you aren't actually executing the filter right then and there. You're creating an IEnumerable, which is essentially a blueprint. This is called deferred execution. It's incredibly powerful for performance, but it can bite you if you assume the data is "frozen" the moment you write the line of code.
This is where ToList(), ToArray(), and ToDictionary() come in. These are called "conversion operators." Their primary job is to force the execution of the query immediately and store the results in a concrete data structure. Once you call ToList(), the query is run, the results are fetched, and you have a snapshot of the data in memory. If the source collection changes a millisecond later, your list stays exactly as it was.
Materializing Your Data
You'll find yourself reaching for ToList() most of the time. It's the Swiss Army knife of materialization. Use it when you need to modify the collection later (like adding or removing items) or when you're passing the data to a piece of code that expects a List<T>. I usually suggest ToList() whenever you plan to iterate over the same filtered result more than once; otherwise, C# will re-run the entire LINQ query every single time you use a foreach loop, which is a silent performance killer.
Then there's ToArray(). In most modern apps, the performance difference between a List and an Array is negligible, but ToArray() is your best bet when you want to signal that the size of the collection is fixed. It's slightly more memory-efficient and tells other developers, "This is a finished set of data; don't try to add things to it."
var activeUsers = allUsers .Where(u => u.IsActive) .ToList(); // The query runs NOW. The results are frozen in a List. var topScores = highScores .OrderByDescending(s => s.Value) .Take(10) .ToArray(); // The top 10 are locked into a fixed-size array.Turning Lists into Maps
Finally, there is
ToDictionary(). This is a massive productivity boost when you realize you're doing "lookup loops." If you find yourself writing aforeachloop just to find one specific object in a list by its ID, stop. You should be using a dictionary.
ToDictionary()allows you to specify which property should act as the key and which should be the value. I've used this countless times to optimize API responses—taking a flat list of categories and turning it into a dictionary for O(1) instant lookup. Just be careful: if your source list contains two items with the same key,ToDictionary()will throw anArgumentException. It demands uniqueness.// Instead of looping through a list to find a user by ID... var userMap = allUsers.ToDictionary(u => u.UserId); // Now you can jump straight to the user without a loop var myUser = userMap[12345];
📋 Practical Task
Exercise: Building a Fast-Lookup Product Catalog
You are working on an e-commerce backend. You have a list of Product objects, but the checkout process is slow because the system is looping through the entire list every time it needs to find a product's price by its SKU (Stock Keeping Unit).
Your task:
- Create a class
Productwith propertiesstring SKU,string Name, anddecimal Price. - Initialize a
List<Product>with at least five different products. - Use
ToDictionary()to convert that list into a dictionary where theSKUis the key and theProductobject is the value. - Write a small piece of logic that takes a "target SKU" string and uses the dictionary to instantly print the price of that product.
- Ensure your code handles the case where a SKU might not exist in the dictionary to avoid a
KeyNotFoundException.
There are no comments for now.