Clean Code Tips for .NET Developers - Part I
Writing clean code is crucial for development. If writing code is art then writing clean code would be a masterpiece.
Check out the first post of the clean code tips series :
1- Give meaningful names
Using meaningful names makes code self-explanatory, reducing the need for additional comments.
Bad Example :
public class Emp
{
public string n;
public int a;
public void d()
{
Console.WriteLine($"Name: {name}, Age: {age}");
}
}
Good Example :
public class Employee
{
public string name;
public int age;
public void DisplayInfo()
{
Console.WriteLine($"Name: {name}, Age: {age}");
}
}
2- Avoid returning null
Returning null can lead to NullReferenceException errors when the caller attempts to access properties or methods on the returned object. It can also make the code harder to read and maintain, as the caller must always check for null before using the returned value.
Suppose we have this piece of code :
public class UserService
{
public User? GetUserById(int id)
{
if (id <= 0)
{
return null;
}
.... Rest logic of method ...
return user;
}
}
We can improve it with this, instead of returning null we can throw an exception and then handle it separately :
public sealed class UserService
{
public User GetUserById(int id)
{
if (id <= 0)
{
// throws an exception instead of returning null
throw new ArgumentException("Invalid user Id");
}
.... Rest logic of method ...
return user;
}
}
3- Keep class | method size small
A class should have one reason to change.
Smaller classes are easier to manage, test, and maintain.
At some points, this asks us to follow the Single Responsibility Principle.
Suppose we have this example in which we have a lot of code, with different concerns :
public sealed class OrderProcessing
{
public void ProcessOrder(Order order)
{
if (!ValidateOrder(order))
{
throw new ArgumentException("Invalid order");
}
ChargePayment(order);
UpdateInventory(order);
SendConfirmationEmail(order);
LogOrderDetails(order);
}
private bool ValidateOrder(Order order) { }
private void ChargePayment(Order order) { }
private void UpdateInventory(Order order) { }
private void SendConfirmationEmail(Order order) { }
private void LogOrderDetails(Order order) { }
}
This could be improved like this where we have separate classes for each concern :
public sealed class OrderValidator
{
public bool Validate(Order order){}
}
public sealed class PaymentProcessor
{
public void Charge(Order order){}
}
public sealed class InventoryManager
{
public void Update(Order order){}
}
public sealed class EmailService
{
public void SendConfirmation(Order order){}
}
public sealed class OrderProcessing
{
private readonly OrderValidator _validator;
private readonly PaymentProcessor _paymentProcessor;
private readonly InventoryManager _inventoryManager;
private readonly EmailService _emailService;
public OrderProcessing(
OrderValidator validator,
PaymentProcessor paymentProcessor,
InventoryManager inventoryManager,
EmailService emailService)
{
_validator = validator;
_paymentProcessor = paymentProcessor;
_inventoryManager = inventoryManager;
_emailService = emailService;
}
public void ProcessOrder(Order order)
{
if (!_validator.Validate(order))
{
throw new ArgumentException("Invalid order");
}
_paymentProcessor.Charge(order);
_inventoryManager.Update(order);
_emailService.SendConfirmation(order);
}
}
Although there is no hard-code rule about how many lines of code a method should contain, 20-40 lines are enough for a method.
4- Donβt reinvent the wheel
Reinventing the wheel can lead to unnecessary duplication of effort, wasting valuable time and resources that could be better spent on developing unique features.
What exactly is reinventing the wheel, it means when we already have code for some issue and we start writing manually code to solve that cause then we are reinventing the wheel :
public sealed class CustomLogger
{
public void Log(string message)
{
using (var writer = new StreamWriter("log.txt", true))
{
writer.WriteLine($"{DateTime.Now}: {message}");
}
}
}
We can achieve that by just injecting the ILogger interface, considering logging is already configured :
public sealed class Application
{
private readonly ILogger _logger;
public Application(ILogger logger)
{
_logger = logger;
}
}
Read about how to implement logging in .NET :
5- Use appropriate tools/IDE
Using the right tools or IDE enhances productivity by providing features like syntax highlighting, code completion, and error checking, which help catch mistakes early.
Keep reading the new update of Visual Studio, and invest time in learning about existing features of tools as well.
Additionally, these are two practices that I follow in my .NET projects, and I would recommend that you do the same:
1- Enabling Editor Config File
Enabling the editor config file in .NET projects to apply some sort of rules on all code
root = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
2- Adding the props file This props file is used to configure common settings and apply them across all projects for example:
<Project>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
Whenever you're ready, there are 3 ways I can help you:
- Subscribe to my youtube channel : For in-depth tutorials, coding tips, and industry insights.
- Promote yourself to 9,000+ subscribers : By sponsoring this newsletter
- Patreon community : Get access to all of my blogs and articles at one place