projectrules.ai

C# Best Practices and Coding Standards

c-sharpdotnetmicrosoftbackendcoding-standards

Description

This rule file provides a comprehensive guide to C# best practices, coding standards, and common patterns for writing maintainable, performant, and secure code.

Globs

**/*.cs
---
description: This rule file provides a comprehensive guide to C# best practices, coding standards, and common patterns for writing maintainable, performant, and secure code.
globs: **/*.cs
---

# C# Best Practices and Coding Standards

This document provides a comprehensive guide to C# best practices, coding standards, and common patterns for writing maintainable, performant, and secure code. It covers various aspects of C# development, including code organization, common patterns, performance considerations, security best practices, testing approaches, common pitfalls, and tooling.

**Library Information:**
- Name: c-sharp
- Tags: language, microsoft, dotnet, backend

## 1. Code Organization and Structure

A well-organized codebase is crucial for maintainability, scalability, and collaboration. Here are some best practices for organizing your C# code:

### 1.1. Directory Structure Best Practices

*   **Project Root:** Contains the solution file (.sln) and project directories.
*   **Project Directory:** Contains the project file (.csproj), source code, and other project-related files.
    *   `src/`: Contains the main source code.
        *   `Models/`: Data models and DTOs (Data Transfer Objects).
        *   `Services/`: Business logic and service classes.
        *   `Controllers/`: API controllers (if applicable).
        *   `Repositories/`: Data access logic.
        *   `Utilities/`: Helper classes and utility functions.
        *   `Exceptions/`: Custom exception definitions.
        *   `Interfaces/`: Interface definitions for abstraction.
        *   `Configuration/`: Configuration-related classes.
    *   `tests/`: Contains unit tests, integration tests, and end-to-end tests.
    *   `docs/`: Documentation for the project.
    *   `build/`: Build scripts and configuration files.
    *   `Properties/`: Assembly information and project settings.

Example:


MyProject/
├── MyProject.sln
├── MyProject/
│   ├── MyProject.csproj
│   ├── src/
│   │   ├── Models/
│   │   ├── Services/
│   │   ├── Controllers/
│   │   ├── Repositories/
│   │   ├── Utilities/
│   │   ├── Exceptions/
│   │   ├── Interfaces/
│   │   └── Configuration/
│   ├── Properties/
│   │   └── AssemblyInfo.cs
│   └── appsettings.json
├── MyProject.Tests/
│   ├── MyProject.Tests.csproj
│   └── UnitTests/
└── README.md


### 1.2. File Naming Conventions

*   **Classes:** PascalCase (e.g., `MyClass.cs`)
*   **Interfaces:** IPascalCase (e.g., `IMyInterface.cs`)
*   **Enums:** PascalCase (e.g., `MyEnum.cs`)
*   **Structs:** PascalCase (e.g., `MyStruct.cs`)
*   **Delegates:** PascalCase (e.g., `MyDelegate.cs`)
*   **Configuration Files:** appsettings.json, config.xml
*   **Test Files:** `MyClassTests.cs`

### 1.3. Module Organization

*   **Namespaces:** Use namespaces to group related classes and interfaces.  Follow a consistent naming convention for namespaces (e.g., `CompanyName.ProjectName.ModuleName`).
*   **Assemblies:**  Divide large projects into multiple assemblies to improve build times, reduce dependencies, and enable code reuse. Consider functional or domain boundaries when creating assemblies.
*   **NuGet Packages:** Use NuGet packages to manage dependencies and share code across projects.

### 1.4. Component Architecture

*   **Layered Architecture:** Separate the application into distinct layers (e.g., presentation, business logic, data access) to promote separation of concerns and testability.
*   **Microservices Architecture:** For large and complex applications, consider a microservices architecture where the application is composed of small, independent services.
*   **Dependency Injection (DI):** Use DI to manage dependencies between components and improve testability and maintainability. Popular DI containers include Autofac, Ninject, and Microsoft.Extensions.DependencyInjection.

### 1.5. Code Splitting Strategies

*   **By Feature:** Group code related to a specific feature into a separate module or assembly.
*   **By Layer:** Separate code based on the architectural layer (e.g., presentation, business logic, data access).
*   **By Responsibility:**  Split classes and methods into smaller, more focused units of work.
*   **Partial Classes:** Use partial classes to split a large class into multiple files for better organization (use sparingly).

## 2. Common Patterns and Anti-patterns

Understanding common design patterns and anti-patterns is essential for writing effective and maintainable C# code.

### 2.1. Design Patterns

*   **Singleton:** Ensures that a class has only one instance and provides a global point of access to it.
*   **Factory:** Provides an interface for creating objects without specifying their concrete classes.
*   **Abstract Factory:** Provides an interface for creating families of related objects without specifying their concrete classes.
*   **Builder:** Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
*   **Observer:** Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
*   **Strategy:** Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
*   **Dependency Injection (DI):**  A technique whereby one object (or static method) supplies the dependencies of another object. This helps to decouple components.
*   **Repository:** Mediates between the domain and data mapping layers, acting like an in-memory domain object collection.
*   **Unit of Work:**  Maintains a list of objects affected by a business transaction and coordinates the writing out of changes.
*   **Asynchronous Programming Patterns (TAP, EAP, APM):**  Handle asynchronous operations efficiently using async/await.

### 2.2. Recommended Approaches for Common Tasks

*   **String Manipulation:** Use `StringBuilder` for efficient string concatenation in loops.
*   **File I/O:** Use `using` statements or `try-finally` blocks to ensure proper disposal of file resources.
*   **Data Access:** Use ORMs like Entity Framework Core or Dapper for simplified data access.
*   **Asynchronous Operations:** Use `async` and `await` for non-blocking asynchronous operations.
*   **Configuration Management:** Use `Microsoft.Extensions.Configuration` for managing application configuration.
*   **Logging:** Use logging frameworks like Serilog or NLog for structured logging.

### 2.3. Anti-patterns and Code Smells

*   **God Class:** A class that does too much and has too many responsibilities.
*   **Long Method:** A method that is too long and complex.
*   **Feature Envy:** A method that accesses data from another object more than its own.
*   **Shotgun Surgery:** Changes to one part of the code require changes to many other parts.
*   **Data Clump:** Groups of data that appear together in multiple places.
*   **Primitive Obsession:** Using primitive types instead of creating custom classes for domain concepts.
*   **Switch Statements (over Polymorphism):** Using large switch statements instead of leveraging polymorphism.
*   **Magic Numbers/Strings:** Hardcoding values directly in the code instead of using constants or configuration settings.
*   **Ignoring Exceptions:** Catching exceptions without handling them properly.
*   **Empty Catch Blocks:** Catching exceptions and doing nothing.
*   **Over-commenting:** Writing excessive comments that are obvious from the code itself.
*   **Dead Code:** Code that is never executed.

### 2.4. State Management Best Practices

*   **Stateless Services:** Design services to be stateless whenever possible to improve scalability and reliability.
*   **Session State:** Use session state sparingly and only when necessary. Consider using distributed caching for session state in web applications.
*   **Caching:** Use caching to improve performance by storing frequently accessed data in memory.
*   **Redux/Flux:** For complex UI applications, consider using a state management library like Redux or Flux.
*   **Immutable Data Structures:** Use immutable data structures to simplify state management and prevent unintended side effects.

### 2.5. Error Handling Patterns

*   **Try-Catch-Finally:** Use `try-catch-finally` blocks to handle exceptions and ensure proper resource cleanup.
*   **Exception Filters:** Use exception filters to catch specific exceptions based on certain conditions.
*   **Custom Exceptions:** Create custom exception types for specific error conditions in your application.
*   **Logging Exceptions:** Log exceptions with sufficient context to aid in debugging.
*   **Graceful Degradation:** Handle errors gracefully and provide informative error messages to the user.
*   **Throw Early, Catch Late:** Detect errors as early as possible and handle exceptions at a higher level.

## 3. Performance Considerations

Optimizing C# code for performance is crucial for creating responsive and efficient applications.

### 3.1. Optimization Techniques

*   **Avoid Boxing and Unboxing:** Boxing and unboxing value types can be expensive. Use generics to avoid boxing and unboxing.
*   **Use Value Types When Appropriate:** Value types (structs) can be more efficient than reference types (classes) for small, immutable data structures.
*   **Minimize Object Allocation:** Object allocation can be expensive. Reuse objects whenever possible.
*   **Use `StringBuilder` for String Concatenation:** `StringBuilder` is more efficient than string concatenation using the `+` operator, especially in loops.
*   **Optimize LINQ Queries:** Use LINQ carefully and avoid unnecessary iterations or computations.  Consider using `AsParallel()` for parallel processing of large collections (with caution, as parallelism introduces complexity).
*   **Use Asynchronous Programming:** Use `async` and `await` to avoid blocking the main thread and improve responsiveness.
*   **Avoid Excessive Locking:** Minimize the use of locks to prevent contention and improve concurrency.
*   **Use Lazy Initialization:** Initialize objects only when they are needed to avoid unnecessary initialization overhead.
*   **Profile Your Code:** Use profiling tools to identify performance bottlenecks and optimize accordingly.  Visual Studio Profiler, dotTrace, and PerfView are good options.

### 3.2. Memory Management Considerations

*   **Garbage Collection:** Understand how the garbage collector works and avoid creating excessive garbage.
*   **Dispose of Resources:** Implement the `IDisposable` interface and use `using` statements to ensure proper disposal of resources (e.g., file streams, database connections).
*   **Weak References:** Use weak references to hold references to objects without preventing them from being garbage collected.
*   **Object Pooling:** Use object pooling to reuse objects and reduce allocation overhead.
*   **Large Object Heap (LOH):** Be aware of the Large Object Heap and avoid allocating large objects unnecessarily.

### 3.3. Rendering Optimization (If Applicable)

*   **UI Virtualization:** Use UI virtualization to render only the visible items in a large list or grid.
*   **Reduce Overdraw:** Minimize the number of times pixels are drawn on top of each other.
*   **Batch Rendering:** Batch rendering operations to reduce the number of draw calls.
*   **Use Hardware Acceleration:** Use hardware acceleration to offload rendering tasks to the GPU.

### 3.4. Bundle Size Optimization (If Applicable)

*   **Tree Shaking:** Remove unused code from the bundle.
*   **Code Minification:** Minify code to reduce its size.
*   **Code Compression:** Compress code using Gzip or Brotli.
*   **Image Optimization:** Optimize images to reduce their size without sacrificing quality.

### 3.5. Lazy Loading Strategies

*   **Lazy Initialization:** Use `Lazy<T>` to initialize objects only when they are accessed.
*   **Virtual Proxy:** Use a virtual proxy to load related data only when it is needed.
*   **Explicit Loading:** Load related data explicitly using methods like `Include` in Entity Framework Core.

## 4. Security Best Practices

Security should be a primary concern in C# development to protect against vulnerabilities and attacks.

### 4.1. Common Vulnerabilities and Prevention

*   **SQL Injection:** Parameterize database queries to prevent SQL injection attacks.
*   **Cross-Site Scripting (XSS):** Encode user input to prevent XSS attacks.
*   **Cross-Site Request Forgery (CSRF):** Use anti-forgery tokens to prevent CSRF attacks.
*   **Authentication and Authorization Vulnerabilities:** Implement secure authentication and authorization mechanisms.
*   **Insecure Direct Object References (IDOR):** Validate user access to objects to prevent IDOR attacks.
*   **File Upload Vulnerabilities:** Validate file types and sizes to prevent malicious file uploads.
*   **Denial-of-Service (DoS) Attacks:** Implement rate limiting and other measures to prevent DoS attacks.
*   **Deserialization Vulnerabilities:** Avoid deserializing untrusted data.
*   **Dependency Vulnerabilities:** Regularly update dependencies to patch security vulnerabilities.

### 4.2. Input Validation

*   **Validate All User Input:** Validate all user input on both the client and server side.
*   **Use Regular Expressions:** Use regular expressions to validate input formats.
*   **Sanitize Input:** Sanitize input to remove or escape potentially malicious characters.
*   **Use Whitelisting:** Use whitelisting to allow only known good input values.
*   **Limit Input Length:** Limit the length of input fields to prevent buffer overflows.

### 4.3. Authentication and Authorization

*   **Use Strong Passwords:** Enforce strong password policies and use password hashing algorithms like bcrypt.
*   **Implement Multi-Factor Authentication (MFA):** Use MFA to add an extra layer of security to the authentication process.
*   **Use Role-Based Access Control (RBAC):** Use RBAC to control user access to resources based on their roles.
*   **Use Claims-Based Authentication:** Use claims-based authentication to represent user identities and permissions.
*   **Implement Proper Session Management:** Implement secure session management practices, including session timeouts and secure cookies.
*   **OAuth 2.0 and OpenID Connect:** Leverage industry-standard protocols for authentication and authorization.

### 4.4. Data Protection

*   **Encrypt Sensitive Data:** Encrypt sensitive data at rest and in transit.
*   **Use Key Management:** Use a secure key management system to store and manage encryption keys.
*   **Protect Connection Strings:** Protect connection strings and other sensitive configuration data.
*   **Implement Auditing:** Implement auditing to track user actions and detect security breaches.

### 4.5. Secure API Communication

*   **Use HTTPS:** Use HTTPS to encrypt communication between the client and server.
*   **Implement API Authentication:** Use API keys, JWTs, or other authentication mechanisms to secure API endpoints.
*   **Rate Limiting:** Implement rate limiting to prevent API abuse.
*   **Input Validation:** Validate all API input to prevent injection attacks.
*   **Output Encoding:** Encode API output to prevent XSS attacks.

## 5. Testing Approaches

Testing is a critical part of the software development process and helps ensure the quality and reliability of C# applications.

### 5.1. Unit Testing

*   **Test Individual Units of Code:** Unit tests should focus on testing individual classes, methods, or functions in isolation.
*   **Use Mocking Frameworks:** Use mocking frameworks like Moq or NSubstitute to isolate units of code and simulate dependencies.
*   **Follow the Arrange-Act-Assert Pattern:** Arrange the test data, act on the code under test, and assert the expected results.
*   **Write Clear and Concise Tests:** Write tests that are easy to read and understand.
*   **Test Edge Cases and Error Conditions:** Test edge cases and error conditions to ensure that the code handles them properly.
*   **Aim for High Code Coverage:** Aim for high code coverage to ensure that most of the code is tested.

### 5.2. Integration Testing

*   **Test Interactions Between Components:** Integration tests should focus on testing the interactions between different components or modules of the application.
*   **Use Real Dependencies or Test Doubles:** Use real dependencies or test doubles to simulate the environment in which the components will operate.
*   **Test Data Access Logic:** Test data access logic to ensure that it interacts correctly with the database.
*   **Test API Endpoints:** Test API endpoints to ensure that they handle requests and responses correctly.

### 5.3. End-to-End Testing

*   **Test the Entire Application Flow:** End-to-end tests should focus on testing the entire application flow from start to finish.
*   **Use Automation Frameworks:** Use automation frameworks like Selenium or Playwright to automate end-to-end tests.
*   **Test User Interfaces:** Test user interfaces to ensure that they are functional and user-friendly.
*   **Test Performance and Scalability:** Test performance and scalability to ensure that the application can handle the expected load.

### 5.4. Test Organization

*   **Create Separate Test Projects:** Create separate test projects for unit tests, integration tests, and end-to-end tests.
*   **Organize Tests by Feature or Module:** Organize tests by feature or module to improve maintainability.
*   **Use Descriptive Test Names:** Use descriptive test names that clearly indicate what the test is verifying.
*   **Use Test Categories:** Use test categories to group related tests together.

### 5.5. Mocking and Stubbing

*   **Use Mocking Frameworks:** Use mocking frameworks like Moq or NSubstitute to create mock objects and stub dependencies.
*   **Create Mock Objects for Dependencies:** Create mock objects for dependencies that are difficult or time-consuming to set up.
*   **Stub Method Calls:** Stub method calls to return specific values or throw exceptions.
*   **Verify Method Calls:** Verify that methods are called with the expected arguments.

## 6. Common Pitfalls and Gotchas

Being aware of common pitfalls and gotchas can help developers avoid mistakes and write more robust C# code.

### 6.1. Frequent Mistakes

*   **NullReferenceException:** Not handling null values properly.
*   **Incorrect Use of Asynchronous Programming:** Blocking the main thread with synchronous operations.
*   **Memory Leaks:** Not disposing of resources properly.
*   **Concurrency Issues:** Race conditions, deadlocks, and other concurrency issues.
*   **Unhandled Exceptions:** Not catching or logging exceptions properly.
*   **SQL Injection:** Not parameterizing database queries.
*   **XSS Attacks:** Not encoding user input properly.
*   **CSRF Attacks:** Not using anti-forgery tokens.
*   **Ignoring Code Analysis Warnings:** Ignoring warnings from the compiler or code analysis tools.

### 6.2. Edge Cases

*   **Empty Collections:** Handling empty collections properly.
*   **Zero Values:** Handling zero values properly.
*   **Maximum and Minimum Values:** Handling maximum and minimum values properly.
*   **Date and Time Zones:** Handling date and time zones correctly.
*   **Unicode Characters:** Handling Unicode characters correctly.

### 6.3. Version-Specific Issues

*   **C# Language Features:** Being aware of new language features and how they affect existing code.
*   **.NET Framework vs. .NET Core vs. .NET:** Understanding the differences between the different .NET platforms.
*   **Breaking Changes:** Being aware of breaking changes in new versions of the .NET framework.

### 6.4. Compatibility Concerns

*   **Cross-Platform Compatibility:** Ensuring that the code works correctly on different operating systems.
*   **Backward Compatibility:** Ensuring that the code is compatible with older versions of the .NET framework.
*   **Interoperability with Other Languages:** Ensuring that the code can interoperate with other languages like C++ or Java.

### 6.5. Debugging Strategies

*   **Use the Visual Studio Debugger:** Use the Visual Studio debugger to step through code, inspect variables, and set breakpoints.
*   **Use Logging:** Use logging to track the execution flow of the code and log important information.
*   **Use Unit Tests:** Use unit tests to isolate and debug individual units of code.
*   **Use Code Analysis Tools:** Use code analysis tools to identify potential problems in the code.
*   **Use Profiling Tools:** Use profiling tools to identify performance bottlenecks.

## 7. Tooling and Environment

Choosing the right tools and configuring the development environment properly can significantly improve productivity and code quality.

### 7.1. Recommended Development Tools

*   **Visual Studio:** The primary IDE for C# development.
*   **Visual Studio Code:** A lightweight code editor with C# support.
*   **JetBrains Rider:** A cross-platform .NET IDE.
*   **NuGet Package Manager:** For managing dependencies.
*   **Git:** For version control.
*   **Resharper:** Powerful Visual Studio extension for code analysis, refactoring, and navigation.

### 7.2. Build Configuration

*   **Use MSBuild or dotnet CLI:** Use MSBuild or the dotnet CLI to build the project.
*   **Configure Build Configurations:** Configure different build configurations for debug and release builds.
*   **Use NuGet Package Restore:** Use NuGet package restore to automatically download dependencies during the build process.
*   **Sign Assemblies:** Sign assemblies to prevent tampering.
*   **Generate Documentation:** Generate documentation from XML comments.
*   **Enable deterministic builds:** Ensure builds are reproducible by enabling deterministic builds in the project file.

### 7.3. Linting and Formatting

*   **Use StyleCop Analyzers:** Use StyleCop Analyzers to enforce coding style rules.
*   **Use Roslyn Analyzers:** Use Roslyn analyzers to detect potential problems in the code.
*   **Configure EditorConfig:** Use EditorConfig to define coding style rules for the project.
*   **Use Code Formatters:** Use code formatters like the Visual Studio code formatter to automatically format code.

### 7.4. Deployment Best Practices

*   **Choose a Deployment Target:** Choose a deployment target based on the application's requirements (e.g., Azure App Service, AWS Elastic Beanstalk, Docker).
*   **Use a Deployment Pipeline:** Use a deployment pipeline to automate the deployment process.
*   **Configure Application Settings:** Configure application settings properly for the deployment environment.
*   **Monitor Application Health:** Monitor application health to detect and resolve issues.

### 7.5. CI/CD Integration

*   **Use a CI/CD Platform:** Use a CI/CD platform like Azure DevOps, GitHub Actions, or Jenkins to automate the build, test, and deployment process.
*   **Configure Build Triggers:** Configure build triggers to automatically start builds when code is committed.
*   **Run Unit Tests and Integration Tests:** Run unit tests and integration tests as part of the build process.
*   **Deploy to Different Environments:** Deploy the application to different environments (e.g., development, staging, production) as part of the deployment pipeline.
*   **Automated Code Reviews:** Integrate with code review tools to automate aspects of code review.

By following these best practices and coding standards, developers can write C# code that is maintainable, performant, secure, and reliable.
C# Best Practices and Coding Standards