Writing Clear and Concise Code: 11 Guidelines for C# Developers

andytechdev

Andy

Posted on June 28, 2023

Writing Clear and Concise Code: 11 Guidelines for C# Developers

Writing clear and concise code is a fundamental skill that we developers should strive to master. Clean code is not only easier to read and understand but also promotes maintainability, collaboration, and efficient debugging.

I could list all of the qualities that I notice in clean code, but there is one overarching quality that leads to all of them. Clean code always looks like it was written by someone who cares. There is nothing obvious that you can do to make it better. All of those things were thought about by the code’s author, and if you try to imagine improvements, you’re led back to where you are, sitting in appreciation of the code someone left for you — code left by someone who cares deeply about the craft. Michael Feathers, author of Working Effectively with Legacy Code


Let’s look at some examples:

Overcomplicating Code

Bad way

public string GetFormattedName(string firstName, string middleName)
{
   string formattedName = string.Empty;

   if (!string.IsNullOrEmpty(firstName))
   {
      formattedName += firstName.Trim();
   }

   if (!string.IsNullOrEmpty(middleName))
   {
      if (!string.IsNullOrEmpty(formattedName))
      {
         formattedName += " ";
      }

      formattedName += middleName.Trim();
   }

   return formattedName;
}
Enter fullscreen mode Exit fullscreen mode

Good way

public string GetFormattedName(string firstName, string middleName)
 => string.Join(" ", firstName?.Trim(), middleName?.Trim()).Trim();
Enter fullscreen mode Exit fullscreen mode

In the simplified code, the string.Join method is used to concatenate the names with spaces, and the ?. null-conditional operator is used to handle potential null values. The code is more concise, easier to read, and achieves the same functionality without unnecessary complexity.

Overcomplicating code can lead to increased cognitive load, reduced maintainability, and higher chances of introducing bugs. It’s important to strive for simplicity and readability in code whenever possible. Let's take a look at another example.

Bad way

bool isEven = (number % 2 == 0) ? true : false;

Enter fullscreen mode Exit fullscreen mode

Good way

bool isEven = number % 2 == 0;

Enter fullscreen mode Exit fullscreen mode

Loops Instead of LINQ

90% of cases loops can be substituted with a LINQ query. It might be my personal viewpoint, but I believe LINQ query is much more comprehensible than a significantly larger for-loop.

Bad way

public List<int> GetEvenNumbers(List<int> numbers)
{
   List<int> evenNumbers = new List<int>();

   foreach (int number in numbers)
   {
      if (number % 2 == 0)
      {
         evenNumbers.Add(number);
      }
   }

   return evenNumbers;
}
Enter fullscreen mode Exit fullscreen mode

Good way

public List<int> GetEvenNumbers(List<int> numbers) 
 => numbers.Where(number => number % 2 == 0).ToList();

Enter fullscreen mode Exit fullscreen mode

Complex IF Condition

A complex if condition refers to a conditional statement that involves multiple logical operations, conditions, or an expression that might be difficult to read and maintain.

Bad way

if (number % 2 == 0)
{
   // Do something
}
Enter fullscreen mode Exit fullscreen mode

Better way

private bool IsEven(int number) => number % 2 == 0;

if (IsEven(number))
   {
     // Do something
   }
}
Enter fullscreen mode Exit fullscreen mode

This practice helps in reducing code duplication, improving code organization, and enhancing code maintainability.

Deep Nesting

In the example below, the code is 3-level nested and this can make the code harder to understand and maintain. Deep nesting can also increase the risk of introducing logical errors and make code modifications more challenging.

Bad way

if (condition1)
{
    if (condition2)
    {
        if (condition3)
        {
            // Do something
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Good way

if (condition1 && condition2 && condition3)
{
    // Do something
}
Enter fullscreen mode Exit fullscreen mode

Just ensure that the conditions are logically correct and properly grouped. Also, it might be a perfect time to refactor and extract them into methods as we did in the previous example IsEven.

String Concatenation

The GenerateGreeting method concatenates multiple strings using the + operator. While this code may work correctly, it can become problematic when dealing with more complex scenarios or a large number of concatenated strings

Bad way

public string GenerateGreeting(string name)
{ 
   return "Hello, " + name + "! " + "How are you today?";
}
Enter fullscreen mode Exit fullscreen mode

Good way

public string GenerateGreeting(string name)
{ 
   return $"Hello, {name}! How are you today?";
}
Enter fullscreen mode Exit fullscreen mode

Magic Numbers

Avoid using magic numbers. Assign meaningful constants or enumerations to such values to improve code clarity and maintainability.

Bad way

if (status == 2)
{
    // Do something
}
Enter fullscreen mode Exit fullscreen mode

Good way

const int InProgressStatus = 2;

if (status == InProgressStatus)
{
    // Do something
}
Enter fullscreen mode Exit fullscreen mode

Long Methods

Break down long methods into smaller, focused methods. This improves code readability, reusability, and maintainability.

Bad way

public void ProcessData()
{
    // Several hundred lines of code
}
Enter fullscreen mode Exit fullscreen mode

Good way

public void ProcessData()
{
    ValidateInput();
    CalculateValues();
    SaveData();
}
Enter fullscreen mode Exit fullscreen mode

Poor Naming Conventions

Using meaningful and descriptive names for variables, methods, and classes improves code readability and helps others understand our code.

Bad way

var dt = DateTime.Now.ToString("YYYY/MM/DD");
Enter fullscreen mode Exit fullscreen mode

Good way

var currentDate = DateTime.Now.ToString("YYYY/MM/DD");
Enter fullscreen mode Exit fullscreen mode

Inconsistent Formatting

Consistent and properly indented code enhances readability and reduces confusion.

Bad way

public void PrintNumbers()
{
for (int i = 0;i < 10; i++)
{
Console.WriteLine(i);
}
}
Enter fullscreen mode Exit fullscreen mode

Good way

public void PrintNumbers()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(i);
    }
}
Enter fullscreen mode Exit fullscreen mode

Excessive Comments

Avoid commenting on obvious code. Comments should be used to explain complex logic or clarify code that may be unclear.

Bad way

// Increment counter by 1
counter++;
Enter fullscreen mode Exit fullscreen mode

Good way

counter++;
Enter fullscreen mode Exit fullscreen mode

File-Scoped Nested Namespaces

File-scoped namespaces were introduced in C# 10.0 and allow us to define namespaces directly at the file level without enclosing them in a traditional namespace block

Ok

// File-scoped nested namespace declaration
namespace MyApplication.Utilities
{
    public static class Helper
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

Better way

namespace MyApplication.Utilities;

public static class Helper
{
}
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

By avoiding these bad practices and adopting good coding practices, we can significantly improve the readability, maintainability, and overall quality of our C# code.


Thanks for reading!

Through my articles, I share Tips & Experiences on web development, career, and the latest tech trends. Join me as we explore these exciting topics together. Let’s learn, grow, and create together!

➕More article about Programming, Careers, and Tech Trends.

🔔 Follow me on Medium | Twitter

💖 💪 🙅 🚩
andytechdev
Andy

Posted on June 28, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related