26 Sep 2016
I'd like to share a few tips for writing concise, readable C#. It's worth keeping them in mind if you want to make things easier for anyone maintaining your code.
White space and indentation
Indentation and white space help the reader to parse related code blocks, so keep them consistent. Mismatched indentation can make your code much trickier to understand.
Reduce line length
Shorter lines are more readable.
See what I did there? You can indent fluent interface calls (e.g. LINQ extension methods) to make them a lot easier to scan:
var resultList = items .SelectMany(x => x.IntArray) .Select(x => x) .Distinct() .ToList();
Another common culprit when it comes to lengthy lines is the ternary expression. I find it helpful to break long expressions onto multiple lines:
var intVar = myVal == 1 ? Convert.ToInt32(longVariableName.GetValue()) : new DynamicConfiguration(myVal).GetValue();
In the case above, it might even be clearer to rewrite using
Use implicitly typed variables where possible
Use of the
var keyword reduces noise. There's no need for this:
RatherLengthyClassName myObject = new RatherLengthyClassName();
When you can do this, with no loss of clarity:
var myObject = new RatherLengthyClassName();
The exception to this rule is when it isn't clear what the type will be from the assignment:
Animal dog = GetThing(true);
This contrived example contains a deliberately terrible method name, but I'm sure we've all seen similar code in real codebases…
I'm sure you'll also have seen code like this:
var myObject1 = new ComplicatedThing(true, 3, true, false, true); myObject2.PerformAction(Action.Update, 12, "Joe Bloggs", true);
What do all of those parameter values represent?
There is an argument for not using boolean flags as method parameters at all, but in the real world we're often using APIs we didn't write, or consuming legacy code which can't easily be rewritten.
By using named method arguments, we can make things much clearer:
var myObject1 = new ComplicatedThing( loadChildren: true, maxDepth: 3, loadImages: true, allowUpdates: false, lockDeletion: true ); myObject2.PerformAction( action: Action.Update, id: 12, name: "Joe Bloggs", subscribe: true );
You don't have to specify the name of every parameter, so you can omit them where the meaning is obvious:
myObject3.AddNewItem(itemData, redirect: true);
Commenting: remember “WHY, not WHAT”
As a general rule of thumb, don't use comments to describe what a piece of code does. If you can't tell this by reading the code, you should figure out a simpler approach.
Add comments when you want to explain why you did something. That way, maintainers won't have to spend time trying to figure out what you were thinking when you wrote it.
Remove old code
Don't just comment out old code and leave it there “just in case”. Having to scan large blocks of commented-out code increases cognitive load for future readers. It also makes searching the code less effective, because searches often return matches which are within commented blocks.
If code is no longer used, delete it and make a commit explaining why you did so. If you later discover you need it after all, retrieve it from your version control system—that's what it's for!
© Mark Ashley Bell 2022