Today I'd like to share a few tips for writing readable, concise C# code, reducing the cognitive load for future maintainers. You shouldn't consider these “hard” rules, but it's definitely worth keeping them in mind.
White space and indentation
Indentation and white space help readers to parse related code blocks, so keep them consistent. Mismatched indentation can make your code much more difficult to understand. Following on from this…
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 longer expressions onto two lines:
var intVar = (myVal == 1) ? Convert.ToInt32(ConfigurationManager.AppSettings["Flag"]) : new DynamicConfiguration(myVal).GetValue();
In the above case, it might be even clearer to rewrite the expression as an if/else statement.
Use implicitly typed variables where possible
var keyword to reduce noise when initialising variables. 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 at all clear what the type will be from the assignment:
Animal dog = GetThing(true);
This contrived example contains a deliberately terrible method name, but we've all seen similar in real codebases…
Named method parameters
Doubtless you will have encountered lines like the following:
var myObject1 = new ComplicatedThing(true, 3, true, false, true); myObject2.PerformAction(Action.Update, 12, "Joe Bloggs", true);
But what do all those parameter values represent? Using named method parameters can make things much clearer for maintainers:
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 pass in names for all the parameters, 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 it's not clear from reading the code itself, you should try to simplify it until the meaning is obvious.
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, unused code and leave it there “just in case”. Having to scan past large blocks of unnecessary commented-out code just increases cognitive load.
If code is no longer used, delete it and make a commit explaining why you did so. You can always retrieve it from your version control system if you discover you need it again later.
I hope you find these tips useful! As always, your comments are welcome.