C# Style Guide
Contents:
Properties
Public Properties / Methods are UpperCamelCase
public string SomePublicProp { get; set; }
public readonly string SomeOtherPublicProp = "foo";
public string SomePublicMethod()
{
return "foo";
}
Private Properties are _lowerCamelCase With a Leading Underscore
private string _somePrivateProp { get; set; }
private readonly string _someReadonlyPrivateProp = "Value";
Spacing
In most cases, properties should be spaced with blank lines in between them for improved clarity:
Enum Serialization in C
In C#, when we serialize enums to JSON, they default to their numeric values. This can be confusing for other applications (and developers) trying to interpret these numbers without context. To make things clearer and more intuitive, it's a good idea to serialize enums as strings. This approach not only makes the data more readable but also easier to work with.
Why Serialize Enums?
- Consistency Across Systems: When your application interacts with different systems (like web services, databases, or other applications), ensuring enums are consistently serialized guarantees that all systems interpret these values correctly.
- Maintainability: Serialized enums make code more readable and maintainable. It allows developers to use meaningful names in code while ensuring the underlying value is correctly understood by the system.
- Versioning and Refactoring: As our applications evolve, enums might change. Proper serialization allows for easier versioning and refactoring without breaking existing data or external system integrations.
Best Practices for Enum Serialization
1. Use String Representation:
- Why: More readable and less prone to errors compared to numeric representation. Changes in the order or addition of new values do not affect the serialized data.
-
How: Use
JsonConverteror similar attributes to ensure enums are serialized as strings.
2. Explicitly Define Enum Values:
- Why: This ensures that the serialization values are constant and not impacted by changes in the enum order or additions.
-
How: Assign explicit values to each enum member.
3. Handle Unknown or Default Values:
- Why: To ensure robustness in your application when encountering unexpected or new enum values (especially when dealing with external data sources).
-
How: Implement a default case in your serialization logic or use a special enum value to represent unknown cases.
4. Use Custom Serialization Logic for Complex Scenarios:
- Why: Sometimes the default serialization might not fit your needs, especially in complex scenarios or when dealing with legacy systems.
-
How: Implement custom converters to handle specific serialization and deserialization logic.
Anti Patterns and Best Practices
Exception Masking / Obfuscation
Exception masking occurs when an exception is caught and either not handled properly or replaced by another, obfuscating the original error. This results in losing key details like the stack trace, message, and context, making it harder to debug.
Key issues caused by exception masking include:
- Loss of context: The original exception details, including the true exception type, location and stack trace, are lost entirely, preventing proper debugging after the incident.
- Reduced visibility: Without logging or rethrowing the original exception, important information is suppressed, leading to silent failures.
- Harder debugging: Since the true nature of the error is hidden, identifying and fixing bugs becomes more difficult.
Proper exception handling involves logging or rethrowing the original exception or wrapping it in a new exception while retaining the original one (e.g., using exception chaining). This ensures that the full error context is preserved for debugging.
Example of Exception Masking
try
{
// Some database operation
}
catch (SqlException)
{
//Here, the original SqlException is lost when the InvalidOperationException is thrown,
throw new InvalidOperationException("Operation failed.");
}
Solution: Use Proper Exception Chaining
To avoid exception masking, the original exception should be passed along as part of the new exception, preserving the original context and stack trace.