C# Best Practices
Learn industry-standard practices and patterns to write clean, maintainable, and efficient C# code.
All Best Practices
Coding Standards
9 practicesChoose descriptive names for variables, methods, and classes that clearly express their purpose.
// Good
public class CustomerOrderProcessor
{
    private readonly IPaymentService _paymentService;
    
    public async Task<OrderResult> ProcessCustomerOrderAsync(Order order)
    {
        // Implementation
    }
}// Bad
public class COP
{
    private readonly IPS ps;
    
    public async Task<OR> ProcessAsync(O o)
    {
        // Implementation
    }
}Use PascalCase for public members, camelCase for private fields, and consistent naming patterns.
// Good
public class ProductService
{
    private readonly ILogger _logger;
    private const int MaxRetryAttempts = 3;
    
    public async Task<Product> GetProductByIdAsync(int productId)
    {
        // Implementation
    }
}// Bad
public class productservice
{
    private readonly ILogger Logger;
    private const int max_retry_attempts = 3;
    
    public async Task<Product> getproductbyid(int ProductID)
    {
        // Implementation
    }
}Maintain consistent indentation throughout your code to improve readability and maintainability.
// Good
public class Example
{
    public void Method()
    {
        if (condition)
        {
            // Do something
        }
    }
}// Bad
public class Example
{
public void Method()
{
if (condition)
{
// Do something
}
}
}Keep line length to a reasonable limit (e.g., 80-100 characters) to enhance code readability.
// Good
public class Example
{
    public void Method()
    {
        var longString = "This is a long string that is split " +
                         "across multiple lines for better readability.";
    }
}// Bad
public class Example
{
    public void Method()
    {
        var longString = "This is a long string that is not split across multiple lines, making it hard to read.";
    }
}Use named constants instead of magic numbers to make your code more understandable and maintainable.
// Good
public class Circle
{
    private const double Pi = 3.14159;
    public double CalculateCircumference(double radius)
    {
        return 2 * Pi * radius;
    }
}// Bad
public class Circle
{
    public double CalculateCircumference(double radius)
    {
        return 2 * 3.14159 * radius;
    }
}Write comments to explain why code exists, not what it does. Keep comments up-to-date.
// Good
public class DataProcessor
{
    // This method processes data from the input stream and applies necessary transformations.
    public void ProcessData(Stream inputStream)
    {
        // Implementation
    }
}// Bad
public class DataProcessor
{
    // Process data
    public void ProcessData(Stream inputStream)
    {
        // Implementation
    }
}Encapsulate complex conditionals in methods to improve readability and maintainability.
// Good
public class Order
{
    public bool IsEligibleForDiscount(Customer customer)
    {
        return customer.IsLoyal && customer.TotalOrders > 10;
    }
}// Bad
public class Order
{
    public bool IsEligibleForDiscount(Customer customer)
    {
        return customer.IsLoyal && customer.TotalOrders > 10 && customer.HasCoupon;
    }
}Implement proper exception handling to manage errors gracefully and maintain application stability.
// Good
public class FileProcessor
{
    public void ProcessFile(string filePath)
    {
        try
        {
            // File processing logic
        }
        catch (IOException ex)
        {
            // Handle I/O exceptions
        }
    }
}// Bad
public class FileProcessor
{
    public void ProcessFile(string filePath)
    {
        // File processing logic without exception handling
    }
}Use a consistent logging framework to log errors with sufficient context to aid in debugging.
// Good
public class UserService
{
    private readonly ILogger<UserService> _logger;
    public UserService(ILogger<UserService> logger)
    {
        _logger = logger;
    }
    public void CreateUser(User user)
    {
        try
        {
            // User creation logic
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating user {UserId}", user.Id);
        }
    }
}// Bad
public class UserService
{
    public void CreateUser(User user)
    {
        try
        {
            // User creation logic
        }
        catch (Exception)
        {
            Console.WriteLine("An error occurred.");
        }
    }
}Performance
8 practicesWhen concatenating multiple strings, especially in loops, use StringBuilder for better performance.
// Good
var sb = new StringBuilder();
foreach (var item in items)
{
    sb.AppendLine($"Item: {item.Name}");
}
string result = sb.ToString();// Bad
string result = "";
foreach (var item in items)
{
    result += $"Item: {item.Name}\n";
}Use asynchronous programming for I/O bound operations to improve scalability.
// Good
public async Task<string> GetDataAsync()
{
    using var client = new HttpClient();
    return await client.GetStringAsync("https://api.example.com/data");
}// Bad
public string GetData()
{
    using var client = new HttpClient();
    return client.GetStringAsync("https://api.example.com/data").Result;
}Use efficient LINQ queries to minimize performance overhead and improve execution speed.
// Good
var result = data.Where(x => x.IsActive).Select(x => x.Name).ToList();
// Use method syntax for better performance
var result = (from x in data
              where x.IsActive
              select x.Name).ToList();// Bad var result = data.Select(x => x.Name).Where(x => x.IsActive).ToList();
Utilize parallel processing to perform tasks concurrently and improve application throughput.
// Good
Parallel.ForEach(data, item =>
{
    ProcessItem(item);
});// Bad
foreach (var item in data)
{
    ProcessItem(item);  // Sequential processing
}Choose the right data structures for your needs to improve performance and memory usage.
// Good
List<int> numbers = new List<int>();
for (int i = 0; i < 1000; i++)
{
    numbers.Add(i);
}// Bad
ArrayList numbers = new ArrayList();
for (int i = 0; i < 1000; i++)
{
    numbers.Add(i);
}Avoid unnecessary boxing and unboxing to reduce overhead and improve performance.
// Good int number = 123; object obj = number; // Boxing int unboxedNumber = (int)obj; // Unboxing // Better int number = 123; int sameNumber = number;
// Bad int number = 123; object obj = number; // Boxing int unboxedNumber = (int)obj; // Unboxing
Implement caching to store frequently accessed data and reduce computation time.
// Good
public class DataService
{
    private readonly IMemoryCache _cache;
    public DataService(IMemoryCache cache)
    {
        _cache = cache;
    }
    public string GetData(string key)
    {
        if (!_cache.TryGetValue(key, out string data))
        {
            data = FetchDataFromSource(key);
            _cache.Set(key, data);
        }
        return data;
    }
}// Bad
public class DataService
{
    public string GetData(string key)
    {
        return FetchDataFromSource(key);  // No caching
    }
}Reuse objects instead of creating new ones to reduce memory usage and improve performance.
// Good
public class ConnectionManager
{
    private readonly SqlConnection _connection = new SqlConnection();
    public SqlConnection GetConnection()
    {
        return _connection;
    }
}// Bad
public class ConnectionManager
{
    public SqlConnection GetConnection()
    {
        return new SqlConnection();  // Creates a new connection every time
    }
}Security
15 practicesAlways validate input parameters to prevent security vulnerabilities and unexpected behavior.
// Good
public void ProcessUser(string email, int age)
{
    if (string.IsNullOrWhiteSpace(email))
        throw new ArgumentException("Email cannot be null or empty", nameof(email));
    
    if (age < 0 || age > 150)
        throw new ArgumentOutOfRangeException(nameof(age), "Age must be between 0 and 150");
    
    // Process user
}// Bad
public void ProcessUser(string email, int age)
{
    // No validation - potential security risk
    var user = new User { Email = email, Age = age };
    // Process user
}Ensure all data transmitted between the client and server is encrypted using HTTPS.
// Good
public void Configure(IApplicationBuilder app)
{
    app.UseHttpsRedirection();
    // Other middleware
}// Bad
public void Configure(IApplicationBuilder app)
{
    // No HTTPS redirection
    // Other middleware
}Use authentication and authorization to protect resources and ensure only authorized users can access them.
// Good
services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.Authority = "https://your-auth-server.com";
    options.Audience = "your-audience";
});
services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});// Bad // No authentication or authorization setup services.AddAuthorization();
Always sanitize user input to prevent injection attacks such as SQL injection and cross-site scripting (XSS).
// Good
public void ExecuteQuery(string userInput)
{
    var sanitizedInput = Sanitize(userInput);
    // Use sanitizedInput in database query
}// Bad
public void ExecuteQuery(string userInput)
{
    // Directly using userInput in database query
}Store passwords securely using strong hashing algorithms like bcrypt.
// Good
public void StorePassword(string password)
{
    var hashedPassword = BCrypt.Net.BCrypt.HashPassword(password);
    // Store hashedPassword in database
}// Bad
public void StorePassword(string password)
{
    // Storing plain text password in database
}Keep all libraries and dependencies up to date to protect against known vulnerabilities.
// Good // Regularly check for updates and apply them // Use tools like Dependabot or npm audit to automate this process
// Bad // Ignoring dependency updates // Using outdated libraries with known vulnerabilities
Use rate limiting to protect your application from abuse and denial-of-service attacks.
// Good
public void Configure(IApplicationBuilder app)
{
    app.UseRateLimiting();
    // Other middleware
}// Bad
public void Configure(IApplicationBuilder app)
{
    // No rate limiting
    // Other middleware
}Implement CSP to prevent cross-site scripting (XSS) and other code injection attacks.
// Good
public void Configure(IApplicationBuilder app)
{
    app.UseCsp(options => options
        .DefaultSources(s => s.Self())
        .ScriptSources(s => s.Self().CustomSources("https://trustedscripts.example.com"))
        .StyleSources(s => s.Self().UnsafeInline())
    );
    // Other middleware
}// Bad
public void Configure(IApplicationBuilder app)
{
    // No CSP implementation
    // Other middleware
}Ensure cookies are secure by setting the Secure and HttpOnly flags.
// Good
var cookieOptions = new CookieOptions
{
    Secure = true, // Ensures the cookie is sent over HTTPS only
    HttpOnly = true // Prevents JavaScript access to the cookie
};
Response.Cookies.Append("SessionId", sessionId, cookieOptions);// Bad
var cookieOptions = new CookieOptions
{
    // No Secure or HttpOnly flags
};
Response.Cookies.Append("SessionId", sessionId, cookieOptions);Add security headers like X-Content-Type-Options, X-Frame-Options, and X-XSS-Protection to enhance security.
// Good
public void Configure(IApplicationBuilder app)
{
    app.Use(async (context, next) =>
    {
        context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
        context.Response.Headers.Add("X-Frame-Options", "DENY");
        context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
        await next();
    });
    // Other middleware
}// Bad
public void Configure(IApplicationBuilder app)
{
    // No security headers
    // Other middleware
}Ensure that strong encryption algorithms are used for data protection.
// Good
public void EncryptData(string data)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = GenerateStrongKey();
        // Encryption logic
    }
}// Bad
public void EncryptData(string data)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = GenerateWeakKey();
        // Encryption logic
    }
}Ensure that access controls are in place to restrict access to sensitive data and operations.
// Good
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
    });
}// Bad // No access control policies defined services.AddAuthorization();
Regularly monitor and audit logs to detect and respond to security incidents.
// Good
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    app.Use(async (context, next) =>
    {
        logger.LogInformation("Request: {Method} {Path}", context.Request.Method, context.Request.Path);
        await next();
    });
    // Other middleware
}// Bad
public void Configure(IApplicationBuilder app)
{
    // No logging or monitoring
    // Other middleware
}Perform regular security testing, including vulnerability scans and penetration testing, to identify and address security weaknesses.
// Good // Schedule regular security assessments // Use tools like OWASP ZAP or Nessus for automated scans
// Bad // No regular security testing // Ignoring potential vulnerabilities
Provide regular security training to employees to raise awareness and reduce the risk of human error.
// Good // Conduct regular security workshops and training sessions // Keep employees informed about the latest security threats
// Bad // No security training for employees // Lack of awareness about security best practices
Architecture
13 practicesImplement dependency injection to create loosely coupled, testable code.
// Good
public class OrderService
{
    private readonly IPaymentProcessor _paymentProcessor;
    private readonly IEmailService _emailService;
    
    public OrderService(IPaymentProcessor paymentProcessor, IEmailService emailService)
    {
        _paymentProcessor = paymentProcessor;
        _emailService = emailService;
    }
}// Bad
public class OrderService
{
    private readonly PaymentProcessor _paymentProcessor = new PaymentProcessor();
    private readonly EmailService _emailService = new EmailService();
}Design your application as a collection of loosely coupled services to improve scalability and maintainability.
// Good
public class OrderService
{
    public void ProcessOrder(Order order)
    {
        // Logic to process order
    }
}
// Microservice setup
public class OrderMicroservice
{
    private readonly OrderService _orderService;
    public OrderMicroservice(OrderService orderService)
    {
        _orderService = orderService;
    }
    public void Start()
    {
        // Start microservice
    }
}// Bad
public class MonolithicOrderProcessor
{
    public void ProcessOrder(Order order)
    {
        // Logic to process order
        // All logic in one place
    }
}Implement event-driven architecture to decouple components and improve responsiveness.
// Good
public class EventPublisher
{
    public void PublishEvent(Event event)
    {
        // Logic to publish event
    }
}
public class EventSubscriber
{
    public void Subscribe()
    {
        // Logic to subscribe to events
    }
}// Bad
public class TightlyCoupledComponent
{
    public void Execute()
    {
        // Direct calls to other components
    }
}Use DDD to align your software model with business needs and improve communication between technical and non-technical stakeholders.
// Good
public class CustomerAggregate
{
    public void AddOrder(Order order)
    {
        // Logic to add order to customer
    }
}// Bad
public class Customer
{
    public void AddOrder(Order order)
    {
        // Logic scattered across multiple classes
    }
}Structure your application into layers (e.g., presentation, business, data) to separate concerns and improve maintainability.
// Good
public class PresentationLayer
{
    public void DisplayData()
    {
        // Logic to display data
    }
}
public class BusinessLayer
{
    public void ProcessData()
    {
        // Logic to process data
    }
}// Bad
public class MonolithicApplication
{
    public void Execute()
    {
        // All logic in one place
    }
}Separate the read and write operations of your application to optimize performance and scalability.
// Good
public class CommandHandler
{
    public void HandleCommand(Command command)
    {
        // Logic to handle command
    }
}
public class QueryHandler
{
    public TResult HandleQuery<TResult>(Query query)
    {
        // Logic to handle query
        return default(TResult);
    }
}// Bad
public class UnifiedHandler
{
    public void HandleRequest(Request request)
    {
        // Mixed logic for handling commands and queries
    }
}Implement an API Gateway to manage and route requests to various microservices, providing a single entry point.
// Good
public class ApiGateway
{
    public void RouteRequest(Request request)
    {
        // Logic to route request to appropriate microservice
    }
}// Bad
public class DirectClientAccess
{
    public void AccessService(Service service)
    {
        // Direct access to microservices without a gateway
    }
}Use semantic versioning (MAJOR.MINOR.PATCH) to communicate changes in your API or software effectively.
// Good // Version 1.0.0 - Initial release // Version 1.1.0 - Added new features // Version 2.0.0 - Breaking changes introduced
// Bad // Version 1 - Initial release // Version 2 - Added new features and breaking changes
Ensure your APIs are versioned to manage changes and maintain backward compatibility.
// Good
// API v1
[Route("api/v1/products")]
public IActionResult GetProductsV1()
{
    // Logic for version 1
}
// API v2
[Route("api/v2/products")]
public IActionResult GetProductsV2()
{
    // Logic for version 2
}// Bad
// No versioning
[Route("api/products")]
public IActionResult GetProducts()
{
    // Logic without versioning
}Maintain a changelog to document changes, bug fixes, and updates for each version.
// Good // Changelog // Version 1.0.0 - Initial release // Version 1.1.0 - Added feature X // Version 1.1.1 - Fixed bug Y
// Bad // No changelog // Users are unaware of changes and updates
Provide clear deprecation notices and timelines for phasing out old versions.
// Good // Deprecation notice // API v1 will be deprecated on 2023-12-31 // Please migrate to API v2
// Bad // No deprecation notice // Users are caught off guard by sudden changes
Use feature toggles to manage the release of new features without deploying a new version.
// Good
public class FeatureToggle
{
    public bool IsFeatureXEnabled { get; set; }
    public void ToggleFeatureX()
    {
        // Logic to enable/disable feature X
    }
}// Bad // No feature toggles // New features require a full deployment
Adopt branching strategies like Git Flow or trunk-based development to manage version control effectively.
// Good // Git Flow // - master: production-ready code // - develop: latest development changes // - feature branches: new features
// Bad // No branching strategy // Code changes are made directly to the main branch
Additional Resources
Deepen your understanding with these comprehensive guides
