--- 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` 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.