Part 15: C# Coding Standards and Conventions: A Step-by-Step Refactoring Guide
Maintaining a high standard of code quality is essential for professional .NET development. Following established C# coding standards and conventions ensures that your codebase is consistent, readable, and easy for team members to navigate.
In this guide, we will review core C# conventions and perform a step-by-step refactor of a messy code sample to demonstrate how to apply these standards effectively.
1. Core C# Naming Conventions
The most fundamental part of coding standards is naming. Microsoft provides a set of guidelines that are widely adopted in the .NET ecosystem.
| Element | Convention | Example |
|---|---|---|
| Class, Struct, Interface | PascalCase | UserProfile, IShoppingCart |
| Method | PascalCase | GetOrderDetails() |
| Property | PascalCase | CreatedAt |
| Private Field | camelCase with _ prefix |
_userService |
| Local Variable | camelCase | totalAmount |
| Parameter | camelCase | userId |
| Constant | PascalCase | MaxRetryCount |
2. General Coding Guidelines
Braces and Layout
- Always use braces (
{ }) even for single-line statements to prevent errors during future edits. - Use one blank line between method definitions and property groups to improve visual separation.
- Keep classes and methods focused on a single responsibility.
Modern C# Syntax
- Use
varwhen the type is obvious from the right-hand side of the assignment (e.g.,var list = new List<string>();). - String Interpolation: Use
$"Value: {variable}"instead ofstring.Format()or string concatenation for better readability. - Expression-bodied members: Use
=>for simple one-line methods or properties to reduce boilerplate.
3. The Before Code (Non-Standard)
Here is an example of code that works but violates multiple C# standards and conventions.
using System;
using System.Collections.Generic;
namespace MyProject.Logic
{
public class order_processor
{
private List<string> Items = new List<string>();
public double total_price;
public void add_item(string Name, double p)
{
if (Name != null) {
Items.Add(Name);
total_price += p;
}
}
public void PrintOrder()
{
Console.WriteLine("Order contains " + Items.Count + " items.");
foreach(var i in Items)
{
Console.WriteLine("Item: " + i);
}
Console.WriteLine("Total: " + total_price.ToString());
}
}
}
Issues identified:
- Class Name:
order_processorviolates PascalCase. - Private Field:
Itemsshould be_itemsand use camelCase. - Public Field:
total_priceshould be a property (TotalPrice) and use PascalCase. - Method Name:
add_itemviolates PascalCase. - Parameter Name:
Nameviolates camelCase. - Code Style: Inconsistent indentation, string concatenation, and lack of file-scoped namespace.
4. Step-by-Step Refactoring
Step 1: Apply Naming Conventions
First, we update the class, methods, fields, and parameters to follow the standard casing rules.
public class OrderProcessor
{
private List<string> _items = new List<string>();
public double TotalPrice { get; private set; }
public void AddItem(string name, double price)
{
// ...
}
}
Step 2: Modernize Syntax and Layout
Next, we use file-scoped namespaces, string interpolation, and target-typed new().
namespace MyProject.Logic;
public class OrderProcessor
{
private readonly List<string> _items = new();
public void PrintOrder()
{
Console.WriteLine($"Order contains {_items.Count} items.");
// ...
}
}
Step 3: Final Polishing (The After Code)
Here is the fully refactored, standard-compliant version:
using System;
using System.Collections.Generic;
namespace MyProject.Logic;
public class OrderProcessor
{
private readonly List<string> _items = new();
public double TotalPrice { get; private set; }
public void AddItem(string name, double price)
{
ArgumentNullException.ThrowIfNull(name);
_items.Add(name);
TotalPrice += price;
}
public void PrintOrder()
{
Console.WriteLine($"Order contains {_items.Count} items.");
foreach (var item in _items)
{
Console.WriteLine($"Item: {item}");
}
Console.WriteLine($"Total: {TotalPrice:C}");
}
}
5. Summary of Key Improvements
- File-Scoped Namespace: Simplified the file structure.
- Encapsulation: Used a property with a private setter instead of a public field.
- Readonly Intent: Marked the internal list as
readonlyto prevent accidental reassignment. - Guard Clauses: Used
ArgumentNullException.ThrowIfNullfor cleaner validation. - String Interpolation: Improved readability of the output logic.
Leave a comment