DEV Community

Cover image for The 5 Guidelines to Write Cleaner Code in C#
ByteHide
ByteHide

Posted on • Updated on • Originally published at bytehide.com

The 5 Guidelines to Write Cleaner Code in C#

Your C# code should be as easy to read and understand as possible, but this can often be difficult because developers have different styles of coding. That is why from ByteHide we have decided to bring these 5 guidelines will help you to write more readable C# code that can be easily understood by everyone on your team or the ones who are going to work on your project in the future.

The number of lines of code that you need to write can also be reduced with well-designed C# classes and functions, since shorter methods and fewer lines usually improve the understandability of your code.

These tips are adapted from Clean Code Javascript. Thanks Ryan McDermott for your great contribution!


Always encapsulate conditionals

Encapsulation is a way that helps isolate implementation details from the behavior exposed to clients of another class.

Bad way:

if (website.state == "down")
{
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Good way:

if (website.IsDown())
{
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Also, encapsulation allows for greater control over the coupling of the code being written.


Use private/protected members

By not using private/protected members, they are susceptible to being modified (accidentally or unintentionally) and this can cause errors in the code.

We have to keep in mind that we are responsible for the permissions we give in the code. This may seem a bit abstract, but if a string must be read only, specifying that it is read only and not write, is your obligation.

Bad way:

class Car
{
    public string Brand { get; set; }

    public Car(string brand)
    {
        Brand = brand;
    }
}

var car = new Car("Porsche");
Console.WriteLine(car.Brand); // Car brand: Porsche
Enter fullscreen mode Exit fullscreen mode

Good way:

class Car
{
    public string Brand { get; }

    public Car(string brand)
    {
        Brand = brand;
    }
}

var car = new Car("Porsche");
Console.WriteLine(car.Brand); // Car brand: Porsche
Enter fullscreen mode Exit fullscreen mode

By not using set; we prevent it from being accidentally modified in the future.


Learn to use setters and getters

Many times we do not set public, private or protected methods. This prevents us from better controlling the modification of the object's properties.

Bad way:

class BankAccount
{
    public double Balance = 5000;
}

var bankAccount = new BankAccount();

// Buy a cappuccino ☕️ ...
bankAccount.Balance -= 15;
Enter fullscreen mode Exit fullscreen mode

Good way:

class BankAccount
{
    private double _balance = 0.0D;

    pubic double Balance {
        get {
            return _balance;
        }
    }

    public BankAccount(balance = 1000)
    {
       _balance = balance;
    }

    public void WithdrawBalance(int amount)
    {
        if (amount > _balance)
        {
            throw new Exception('Amount greater than available balance.');
        }

        _balance -= amount;
    }

    public void DepositBalance(int amount)
    {
        _balance += amount;
    }
}

var bankAccount = new BankAccount();

// Buy a cappuccino ☕️ ...
bankAccount.WithdrawBalance(price: 15);

balance = bankAccount.Balance;
Enter fullscreen mode Exit fullscreen mode

Also, when inheriting that class, by default, there is the possibility to override that functionality. The possibilities that this allows you are many.


Composition is better than inheritance

Although many do not know whether to use inheritance or composition, let me tell you that it is better to choose to use composition.

At first, many programmers think that inheritance is better but, you always have to ask yourself if composition could somehow model the problem better.

Bad way:

class Car
{
    private string Model { get; set; }
    private string Brand { get; set; }

    public Car(string model, string brand)
    {
        Model = model;
        Brand = brand;
    }

    // ...
}

// Bad because Car "have" engine data.
// CarEngineData is not a type of Car

class CarEngineData : Car
{
   private string Model { get; set; }
   private string Brand { get; set; }

   public CarEngineData(string model, string brand, string displacement, string horses)
    {
         // ...
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode

Good way:

class CarEngineData
{
    public string Displacement { get; }
    public string Horses { get; }

    public CarEngineData(string displacement, string horses)
    {
        Displacement = displacement;
        Horses = horses;
    }

    // ...
}

class Car
{
    public string Model { get; }
    public string Brand { get; }
    public CarEngineData EngineData { get; }

    public Car(string model, string brand)
    {
        Model = model;
        Brand = brand;
    }

    public void SetEngine(string displacement, double horses)
    {
        EngineData = new CarEngineData(displacement, horses);
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode

To know when it is best to use which one, let's look at a quick example:

  • Relationship "is-a" (Human-Animal) Relationship "has-a" (User-UserDetails)

Don't use magic chains

For those who do not know this, magic strings are string values that must be specified in the code. They always generate an impact on the code. In most cases, magic strings end up almost always duplicating themselves and because they cannot be updated automatically, they are susceptible to being a source of errors.

Bad way:

if (userRole == "Admin")
{
    // logic in here
}
Enter fullscreen mode Exit fullscreen mode

Good way:

const string ADMIN_ROLE = "Admin"
if (userRole == ADMIN_ROLE)
{
    // logic in here
}
Enter fullscreen mode Exit fullscreen mode

In this way, we will prevent these strings from being duplicated and causing errors in case of making any change in any of them.

These examples are simplified, if you want to see them in more depth, I recommend you to go to Cleaner Code in C#.

Top comments (4)

Collapse
 
drunkensponge profile image
Marco

Nice. Personally I would add prevent nesting/chaining of parameters (not sure actually how to call this...). So someting like this:

SomeClass.DoSomeStuff(randomObject.CalculateSomething(db.GetData()));
Enter fullscreen mode Exit fullscreen mode

Not only hard to debug, but also very hard to read.

Collapse
 
marissab profile image
Marissa B

Yes! This site needs more car-related example snippets haha. Excellent post. Being careful about inheritance vs composition can be a tricky one when you're doing the initial pass for a design, but hopefully that's worked out on the drawing board before you write a single line of code.

Collapse
 
shawnstoddard profile image
Shawn Stoddard

Good article. Inheritance can be such a trap in many cases. Interfaces are another great, but not always well understood tool.

BTW: you might fix the name of the constructor in your engine data class - I’m betting EmployeeTaxData isn’t what you intended.

Collapse
 
dinerdas profile image
Diner Das

Well described post