|
| 1 | +-- it's a calamitous atrocity -- |
| 2 | + |
| 3 | +If `clients` is an **enumeration (`IEnumerable<T>`)** rather than a **concrete collection (`List<T>`, `T[]`, etc.)**, then every time you iterate over `clients` (e.g. in a `foreach`, `.ToList()`, `.Count()`, etc.), the underlying sequence will be re-evaluated. |
| 4 | + |
| 5 | +### What this means in practice: |
| 6 | + |
| 7 | +* **If `GetClients()` returns a query** (like an EF Core LINQ query, or something built on `yield return`): |
| 8 | + |
| 9 | + * Each enumeration re-runs the logic, potentially hitting the database or recomputing values. |
| 10 | + * Side effects (logging, network calls, DB queries) happen every time you iterate. |
| 11 | + * Performance can degrade unexpectedly. |
| 12 | +* **If it returns a materialized collection** (`List<T>`, `Array`, `HashSet<T>`, etc.): |
| 13 | + |
| 14 | + * No problem. Multiple iterations are just reusing the already-fetched in-memory data. |
| 15 | + |
| 16 | +### Example |
| 17 | + |
| 18 | +```csharp |
| 19 | +var clients = context.Clients.Where(c => c.IsActive); // IQueryable/IEnumerable |
| 20 | +foreach (var c in clients) { ... } // Executes query |
| 21 | +foreach (var c in clients) { ... } // Executes query again! |
| 22 | +``` |
| 23 | + |
| 24 | +vs. |
| 25 | + |
| 26 | +```csharp |
| 27 | +var clients = await context.Clients.Where(c => c.IsActive).ToListAsync(); |
| 28 | +foreach (var c in clients) { ... } // Uses same in-memory list |
| 29 | +foreach (var c in clients) { ... } // Still in-memory, no extra DB call |
| 30 | +``` |
| 31 | + |
| 32 | +### Rule of thumb |
| 33 | + |
| 34 | +If you’re not 100% sure whether the source is materialized, **force it with `.ToList()` or `.ToArray()`** when you assign: |
| 35 | + |
| 36 | +```csharp |
| 37 | +var clients = (await GetClients()).ToList(); |
| 38 | +``` |
| 39 | + |
| 40 | +👉 This guarantees that `clients` is a concrete collection, safe to reuse without surprises. |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +### 2. **Unintended numeric type** |
| 45 | + |
| 46 | +```csharp |
| 47 | +var total = 1 / 2; |
| 48 | +``` |
| 49 | + |
| 50 | +* Type: `int` (since both operands are `int`). |
| 51 | +* Problem: `total` becomes `0`, not `0.5`. If you thought it was `double`, you’ll silently get wrong results. |
| 52 | +* Fix: |
| 53 | + |
| 54 | + ```csharp |
| 55 | + double total = 1 / 2.0; // 0.5 |
| 56 | + ``` |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +So in short: |
| 61 | + |
| 62 | +* With queries, `var` can hide that you’re holding a deferred-execution enumerable instead of actual data. |
| 63 | +* With arithmetic, `var` can lock you into an unintended (smaller or lossy) type. |
| 64 | + |
| 65 | +--- |
| 66 | +Perfect 👍 — here’s a **Top 10 `var` Pitfalls in C# Cheat Sheet**, each with a quick code sample and what can go wrong. |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## 🔟 Common Pitfalls with `var` |
| 71 | + |
| 72 | +### 1. **Deferred Execution (`IEnumerable` vs concrete collection)** |
| 73 | + |
| 74 | +```csharp |
| 75 | +var clients = db.Clients.Where(c => c.IsActive); // IQueryable<Client> |
| 76 | +foreach (var c in clients) { } // DB query executes |
| 77 | +foreach (var c in clients) { } // Executes again! |
| 78 | +``` |
| 79 | + |
| 80 | +💥 Multiple queries when you thought it was cached. |
| 81 | +✅ Fix: `var clients = db.Clients.Where(...).ToList();` |
| 82 | + |
| 83 | +--- |
| 84 | + |
| 85 | +### 2. **Anonymous Types Can’t Escape Scope** |
| 86 | + |
| 87 | +```csharp |
| 88 | +var client = new { Name = "Alice", Age = 30 }; |
| 89 | +``` |
| 90 | + |
| 91 | +💥 Can’t return this type from a method or store in a collection. |
| 92 | +✅ Use a real `Client` class. |
| 93 | + |
| 94 | +--- |
| 95 | + |
| 96 | +### 3. **Async Return Confusion (`Task<T>` vs `T`)** |
| 97 | + |
| 98 | +```csharp |
| 99 | +var text = File.ReadAllTextAsync("file.txt"); // Task<string> |
| 100 | +Console.WriteLine(text.Length); // ❌ Not what you think |
| 101 | +``` |
| 102 | + |
| 103 | +💥 Accidentally working with the `Task`, not the result. |
| 104 | +✅ `var text = await File.ReadAllTextAsync("file.txt");` |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +### 4. **Numeric Overflow / Wrong Type** |
| 109 | + |
| 110 | +```csharp |
| 111 | +var big = 100000 * 100000; // int |
| 112 | +Console.WriteLine(big); // ❌ Overflow |
| 113 | +``` |
| 114 | + |
| 115 | +💥 Silent overflow since it’s `int`. |
| 116 | +✅ `long big = 100000L * 100000;` |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +### 5. **Integer Division Surprise** |
| 121 | + |
| 122 | +```csharp |
| 123 | +var ratio = 1 / 2; // int |
| 124 | +Console.WriteLine(ratio); // 0 |
| 125 | +``` |
| 126 | + |
| 127 | +💥 If you expected a `double`, you’re wrong. |
| 128 | +✅ `double ratio = 1 / 2.0;` |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +### 6. **Boxing and Performance Issues** |
| 133 | + |
| 134 | +```csharp |
| 135 | +var number = GetObject(); // object |
| 136 | +int value = (int)number; // boxing/unboxing |
| 137 | +``` |
| 138 | + |
| 139 | +💥 If you thought it was already `int`, perf and runtime errors appear. |
| 140 | +✅ Declare the concrete type. |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +### 7. **Nullable Value Types Hidden** |
| 145 | + |
| 146 | +```csharp |
| 147 | +var age = dbRow["Age"]; // object (nullable DB field) |
| 148 | +int x = (int)age; // ❌ NullReferenceException |
| 149 | +``` |
| 150 | + |
| 151 | +💥 You think it’s `int`, but it’s really `int?` or `object`. |
| 152 | +✅ Explicitly handle nullable types. |
| 153 | + |
| 154 | +--- |
| 155 | + |
| 156 | +### 8. **Implicitly Wrong Collection Type** |
| 157 | + |
| 158 | +```csharp |
| 159 | +var clients = new[] { "Alice", "Bob" }; // string[] |
| 160 | +``` |
| 161 | + |
| 162 | +💥 If you expected `List<string>`, you lose `List` methods. |
| 163 | +✅ Be explicit: `List<string> clients = new() { "Alice", "Bob" };` |
| 164 | + |
| 165 | +--- |
| 166 | + |
| 167 | +### 9. **Dynamic APIs Returning `object`** |
| 168 | + |
| 169 | +```csharp |
| 170 | +var json = JsonSerializer.Deserialize<object>("{}"); |
| 171 | +``` |
| 172 | + |
| 173 | +💥 You think `json` is a dictionary, but it’s just `object`. Casting headaches follow. |
| 174 | +✅ Explicitly specify generic type: `Deserialize<Dictionary<string, object>>()`. |
| 175 | + |
| 176 | +--- |
| 177 | + |
| 178 | +### 10. **Reduced Readability for Teams** |
| 179 | + |
| 180 | +```csharp |
| 181 | +var x = MethodThatReturnsSomething(); |
| 182 | +``` |
| 183 | + |
| 184 | +💥 Nobody knows what `x` is without hovering or jumping to definition. Maintenance pain. |
| 185 | +✅ Only use `var` when the type is obvious from the right-hand side. |
| 186 | + |
| 187 | +--- |
| 188 | + |
| 189 | +## ✅ Rule of Thumb |
| 190 | + |
| 191 | +* Use `var` when the type is **clear and obvious** (e.g. `var client = new Client();`). |
| 192 | +* Avoid it when the type is **ambiguous, surprising, or critical to correctness** (async, queries, arithmetic). |
| 193 | + |
| 194 | +--- |
0 commit comments