C# advice that sounds genius until you try it
Have you ever read or heard a piece of C# advice and thought, “Hmm, that actually makes a lot of sense”? Until you start using it, and after a while you regret it?
In this blog post, I will talk about some examples of advice that sound smart at first but don’t hold up in practice. They’re not completely useless, but do more harm than good.
I am aware this is a subjective blog post. However, I still believe that the things mentioned here, will be agreed upon with the larger portion of the community. If you have another opinion, that’s fine, I’m just sharing mine. 🙂
Regions
I think most of you are probably familiar with regions in C#, they can be collapsed in your IDE much like collapsing a code block. This feature was designed to help organize your code:
public class Person
{
#region Fields
private string firstName;
private string lastName;
private int age;
#endregion
#region Constructors
public Person(string firstName, string lastName, int age)
{
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
#endregion
// the rest of the code
}
Now, what do I think of them? There absolutely horrible! For starters, since this is just an IDE feature, when viewing your code elsewhere, like on GitHub for example, the collapsibility aspect disappears, leaving you with extra clutter that doesn’t actually help. 🤷🏻
And every time a class gets too big, your instinct shouldn’t be to slap on regions like bandages. It’s like shoving clutter into a closet and shutting the door. Out of sight, out of mind, right? It looks better for a moment, but the problem is still there. Instead, you should refactor your code properly. Split up large classes, extract methods, organize logically. Properly splitting your code is the real solution, don’t just patch it up with regions!
Always using var
Another one you’ll hear a lot is: “Just use var
everywhere.” it’s shorter, the compiler knows the type anyway, and it makes refactoring a breeze. Sounds great, right? Well… not always.
The problem starts when the type isn’t obvious from the right-hand side. If you see something like:
var data = Fetch();
Then what exactly is data
? Without hovering in your IDE or digging into the method, you have no clue. That’s not clean code, that’s a guessing game. 🤔
Now, naming plays a big role here. If you name your variable well, using var
is perfectly fine even when the type isn’t written out. For example:
var customerOrders = GetCustomerOrders();
From the name alone, you get a pretty good idea of what you’re dealing with. The code stays neat, and no one’s left scratching their head.
But even with good naming, there are times when being explicit is better. With primitives like int
, double
, or decimal
, an explicit type can make your intent clearer. For example:
decimal totalPrice = basePrice * taxRate;
This immediately tells you you’re working with a decimal-based calculation, whereas this:
var totalPrice = basePrice * taxRate;
…might leave you wondering what the type actually is, particularly if basePrice
and taxRate
have different types.
So, as a conclusion: var
isn’t evil, but ‘var
everywhere’ isn’t the holy grail either. If the type is obvious or your naming is crystal clear, then by all means, go for var
. But if there’s any chance of confusion, just write the type out. Your future self (and your teammates) will thank you. 🙏🏻
Using exceptions to control flow
You might have heard it before, that you can use exceptions to control the flow of your code. It’s an easy way to skip all the in-between logic and jump right to where you want.” Sounds clever… until you actually do it.
First of all, exceptions are expensive 💸. I’m not just talking about “a little bit slower”, I mean orders of magnitude slower than normal branching. Creating the exception object, capturing the stack trace, and unwinding the call stack takes a lot of work. If you use them as part of your regular logic, you’re burning performance for no good reason.
Second, it’s right there in the name: exception. It’s supposed to be something unusual and well… unexpected. If you’re using them as just another if/else
replacement, you’re misusing the whole concept.
Here’s what I mean:
try
{
ProcessOrder(order);
}
catch (OutOfStockException)
{
NotifyUser("Sorry, that item is out of stock.");
}
This looks harmless, but if “out of stock” is something that happens all the time, this is the wrong tool. You’re basically jumping around your code like it’s a pinball machine, and anyone reading it later has to trace through try/catch blocks just to understand the normal flow.
The better way? Handle these scenario’s gracefully:
if (!IsInStock(order.Item))
{
NotifyUser("Sorry, that item is out of stock.");
return;
}
ProcessOrder(order);
Much clearer, no hidden jumps, and no unnecessary performance hit. Use exceptions for truly unexpected situations, the kind of things you can’t just check with an if
. Anything else is just making your code harder to follow and slower to run. 🐌
Using LINQ for everything
LINQ is awesome. It lets you query and transform collections in a way that feels clean and powerful. You can chain stuff like Where
, Select
, GroupBy
, and Aggregate
together and suddenly you’ve turned ten lines of loops into one ’elegant’ statement.
That’s sounds great, right? One-liners everywhere, so neat and tidy. Well… not really. 🤷🏻
Here is what I mean, let’s say you want to get the top 5 most popular order categories among all active users. You can technically cram that into one big LINQ query:
var topCategories = users
.Where(u => u.IsActive)
.SelectMany(u => u.Orders)
.GroupBy(o => o.Category)
.OrderByDescending(g => g.Count())
.Take(5)
.Select(g => new { g.Key, Count = g.Count() });
Looks fancy. But is it readable? Not really. After every step, the type changes. First you’re working with User
, then with Order
, then with some grouped thing… and you have to mentally keep track of all of that just to follow along.
And if you need to debug this? Good luck setting a breakpoint halfway through that chain. You’ll probably end up splitting it into variables anyway.
Now look at this version:
var activeUsers = users.Where(u => u.IsActive);
var allOrders = activeUsers.SelectMany(u => u.Orders);
var ordersByCategory = allOrders.GroupBy(o => o.Category);
var topCategories = ordersByCategory
.OrderByDescending(g => g.Count())
.Take(5)
.Select(g => new { Category = g.Key, Count = g.Count() });
Yeah, it’s longer. It might use a little more memory and be a touch slower. But you know what? It’s way easier to read 📖. You can see exactly what’s happening at each step, and you can jump in with a breakpoint whenever you want.
That’s the real trap with LINQ: it makes the person writing the code feel smart, but it makes the person reading the code suffer. And since code is read way more often than it’s written, you’re better off spreading things out.
So use LINQ, it’s great, just don’t try to cram everything into one clever-looking chain. If it starts feeling like a puzzle, split it up. A few extra lines of code are sometimes worth it.
These examples are just the ones I see most often, but they’re far from the only cases where ‘smart’ advice backfires. In the end, it’s not about clever shortcuts, it’s about clarity and maintainability. If you keep that in mind, you’ll know for yourself when to follow advice, and when to leave it behind. 💡