.NET Coding Conventions
Introduction
Coding conventions are a set of guidelines for a specific programming language that recommend programming style, practices, and methods. Following established coding conventions makes your code more readable, easier to maintain, and more consistent. In the .NET ecosystem, adhering to consistent coding conventions helps teams collaborate effectively and ensures that your code can be easily understood by other developers.
In this guide, we'll explore the standard coding conventions for .NET languages (primarily focusing on C#), understand why they matter, and learn how to apply them in your projects.
Why Coding Conventions Matter
Before diving into specific conventions, let's understand why they're important:
- Readability - Consistent code style makes reading code easier
- Maintainability - Following conventions makes code maintenance simpler
- Collaboration - Standardized code helps teams work together effectively
- Onboarding - New team members can understand code faster
- Error Prevention - Many conventions help prevent common coding mistakes
Naming Conventions
Pascal Case and Camel Case
.NET uses two primary casing styles:
- PascalCase: First letter of each word is capitalized (e.g.,
CalculateTax
) - camelCase: First letter is lowercase, then first letter of each subsequent word is capitalized (e.g.,
calculatedAmount
)
When to Use Each Case Style
Element | Convention | Example |
---|---|---|
Classes, Records | PascalCase | public class Customer {} |
Interfaces | PascalCase with 'I' prefix | public interface IDisposable {} |
Methods | PascalCase | public void SaveChanges() {} |
Properties | PascalCase | public string FirstName { get; set; } |
Fields (private) | camelCase with underscore prefix | private string _firstName; |
Parameters | camelCase | public void Process(string firstName) {} |
Local variables | camelCase | string fullName = firstName + lastName; |
Constants | PascalCase | public const string DefaultName = "Unknown"; |
Example of Properly Named Code
public class CustomerService : IDisposable
{
private readonly DbContext _context;
private const int MaxRetryAttempts = 3;
public CustomerService(DbContext context)
{
_context = context;
}
public Customer GetCustomerById(int customerId)
{
string cacheKey = $"customer_{customerId}";
return _context.Customers.Find(customerId);
}
public void Dispose()
{
_context.Dispose();
}
}
File Organization Conventions
File Structure
- One class per file (with exceptions for small, related classes)
- File name should match the primary class name (e.g.,
Customer.cs
contains theCustomer
class) - Organize files in directories that follow your namespace structure
Namespace and Using Directives
// System namespaces first
using System;
using System.Collections.Generic;
// Third-party libraries next
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
// Your application namespaces last
using YourCompany.YourProduct.Models;
using YourCompany.YourProduct.Services;
namespace YourCompany.YourProduct.Controllers
{
public class CustomerController
{
// Class implementation
}
}
Code Layout Conventions
Indentation and Spacing
- Use 4 spaces for indentation (not tabs)
- Add a space after keywords like
if
,for
,while
, etc. - Add a space before and after operators (
+
,-
,=
, etc.) - Add a space after commas in method arguments
// Good
if (isActive)
{
int sum = value1 + value2;
DoSomething(sum, value3);
}
// Bad
if(isActive){
int sum=value1+value2;
DoSomething(sum,value3);
}
Braces
.NET typically uses the "Allman style" for braces, where opening braces are on a new line:
// Recommended bracing style
public void ProcessOrder(Order order)
{
if (order == null)
{
throw new ArgumentNullException(nameof(order));
}
foreach (var item in order.Items)
{
ProcessItem(item);
}
}
Exception: You can put the opening brace on the same line for very short single-line blocks:
// Acceptable for very short blocks
if (order == null) { throw new ArgumentNullException(nameof(order)); }
Line Length and Wrapping
Keep lines reasonably short (around 80-120 characters) and use consistent wrapping:
// Long method call with appropriate wrapping
var result = LongMethodNameWithManyParameters(
firstParameter,
secondParameter,
thirdParameter,
forthParameter);
Language-Specific Conventions
C# Specific Conventions
Use Properties Instead of Public Fields
// Good
public string Name { get; set; }
// Bad
public string Name;
Use Object Initializers
// Good
var customer = new Customer
{
FirstName = "John",
LastName = "Doe",
Age = 30
};
// Less preferred
var customer = new Customer();
customer.FirstName = "John";
customer.LastName = "Doe";
customer.Age = 30;
Use String Interpolation
// Good
string greeting = $"Hello, {firstName} {lastName}!";
// Less preferred
string greeting = "Hello, " + firstName + " " + lastName + "!";
Use var
When Appropriate
// Good - type is obvious from right side
var customers = new List<Customer>();
// Good - type is obvious from method name
var customer = GetCustomer(id);
// Avoid when type is not obvious
var result = GetData(); // What type is result?
Use Expression-Bodied Members
// Good for simple methods
public string GetFullName() => $"{FirstName} {LastName}";
// Traditional equivalent
public string GetFullName()
{
return $"{FirstName} {LastName}";
}
Documentation Conventions
XML Documentation
Use XML comments to document public members:
/// <summary>
/// Calculates the total price including tax.
/// </summary>
/// <param name="price">The base price before tax.</param>
/// <param name="taxRate">The tax rate as a decimal (e.g. 0.07 for 7%).</param>
/// <returns>The total price including tax.</returns>
/// <exception cref="ArgumentException">Thrown when price is negative.</exception>
public decimal CalculateTotalPrice(decimal price, decimal taxRate)
{
if (price < 0)
{
throw new ArgumentException("Price cannot be negative", nameof(price));
}
return price * (1 + taxRate);
}
Error Handling Conventions
Validate Parameters
Use guard clauses at the beginning of methods:
public void ProcessOrder(Order order, Customer customer)
{
if (order == null)
throw new ArgumentNullException(nameof(order));
if (customer == null)
throw new ArgumentNullException(nameof(customer));
// Process the order...
}
Exception Handling
public void SaveData(Data data)
{
try
{
_repository.Save(data);
}
catch (DbException ex)
{
_logger.LogError(ex, "Database error occurred while saving data");
throw new DataAccessException("Failed to save data", ex);
}
}
Practical Example: A Complete Class Following Conventions
Let's put all these conventions together in a complete class:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
namespace OnlineStore.Services
{
/// <summary>
/// Provides shopping cart functionality for the online store.
/// </summary>
public class ShoppingCartService : IShoppingCartService
{
private readonly IProductRepository _productRepository;
private readonly ILogger<ShoppingCartService> _logger;
private readonly List<CartItem> _items = new List<CartItem>();
private const decimal DefaultTaxRate = 0.07m;
public ShoppingCartService(
IProductRepository productRepository,
ILogger<ShoppingCartService> logger)
{
_productRepository = productRepository ?? throw new ArgumentNullException(nameof(productRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public IReadOnlyList<CartItem> Items => _items.AsReadOnly();
public int ItemCount => _items.Sum(item => item.Quantity);
/// <summary>
/// Adds a product to the shopping cart.
/// </summary>
/// <param name="productId">The ID of the product.</param>
/// <param name="quantity">The quantity to add.</param>
/// <returns>True if the product was added successfully; otherwise, false.</returns>
public bool AddItem(int productId, int quantity)
{
if (quantity <= 0)
{
_logger.LogWarning("Attempted to add item with invalid quantity: {Quantity}", quantity);
return false;
}
try
{
var product = _productRepository.GetById(productId);
if (product == null)
{
_logger.LogWarning("Product not found: {ProductId}", productId);
return false;
}
var existingItem = _items.FirstOrDefault(i => i.ProductId == productId);
if (existingItem != null)
{
existingItem.Quantity += quantity;
}
else
{
_items.Add(new CartItem
{
ProductId = productId,
ProductName = product.Name,
UnitPrice = product.Price,
Quantity = quantity
});
}
_logger.LogInformation(
"Added {Quantity} of product {ProductId} to cart. Total items: {TotalItems}",
quantity,
productId,
ItemCount);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error adding product {ProductId} to cart", productId);
return false;
}
}
public decimal CalculateTotal() =>
_items.Sum(item => item.UnitPrice * item.Quantity);
public decimal CalculateTotalWithTax(decimal taxRate = DefaultTaxRate) =>
CalculateTotal() * (1 + taxRate);
}
public class CartItem
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public decimal LineTotal => UnitPrice * Quantity;
}
}
Tools for Enforcing Conventions
Several tools can help you maintain coding conventions automatically:
- EditorConfig: Create an
.editorconfig
file to define and enforce coding styles across different editors. - StyleCop: Analyzes C# code for style violations.
- ReSharper/Rider: JetBrains tools that include code style enforcement.
- .NET Analyzers: Built-in static code analyzers that can enforce code quality rules.
Here's a sample .editorconfig
file for a .NET project:
# Top-most EditorConfig file
root = true
# All files
[*]
indent_style = space
indent_size = 4
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# C# files
[*.cs]
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_block_contents = true
# Space preferences
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_parentheses = false
Summary
Following consistent coding conventions in .NET:
- Makes your code more readable and maintainable
- Helps teams collaborate more effectively
- Reduces the cognitive load when reading and understanding code
- Prevents common programming errors
- Makes code reviews more focused on substance rather than style
The conventions we covered include:
- Naming conventions (PascalCase and camelCase)
- File organization
- Code layout and formatting
- Language-specific best practices
- Documentation standards
- Error handling
Remember that while these conventions are widely accepted in the .NET community, the most important thing is consistency within your team or project. Many organizations customize these guidelines to fit their specific needs while maintaining the core principles of readability and maintainability.
Additional Resources
- Microsoft C# Coding Conventions
- .NET Runtime Coding Guidelines
- C# Style Guide by Google
- Book: "Clean Code: A Handbook of Agile Software Craftsmanship" by Robert C. Martin
Exercises
-
Review a piece of your own code and identify places where it doesn't follow the conventions outlined above. Refactor it to adhere to the conventions.
-
Create an
.editorconfig
file for one of your projects to enforce consistent formatting. -
Take the following poorly formatted code and refactor it to follow .NET conventions:
class customer{
public string firstname;
public string lastname;
public int Age;
public List<order> orders=new List<order>();
public void addOrder(order o){orders.Add(o);}
public string getFullName(){return firstname+" "+lastname;}
public decimal calculateTotalSpent(){
decimal total=0;
foreach(var o in orders)total+=o.amount;
return total;
}
}
- Practice writing XML documentation comments for a class or method you've created.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)