C# Code Style Guidelines
Project Configuration
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>Culture-Invariant by Design
Dinaup projects completely ignore the current system culture. This is intentional and prevents an entire class of data conversion errors.
All threads and processes are set to en-US culture to ensure consistent, predictable behavior across all environments. Decimal separators, date formats, and number parsing work identically whether running on a Spanish, German, or American system.
This eliminates bugs caused by culture-dependent conversions and makes data exchange reliable.
DateTime is always UTC. No local times, no timezone conversions, no ambiguity. All dates and times are stored, processed, and transmitted in UTC. This eliminates timezone-related bugs and makes temporal data unambiguous across different geographical locations.
Preferred Practices
Don't Use Nullable Types
Avoid nullable types (e.g., DateTime?, decimal?) unless absolutely necessary. Use default values to represent "empty" states, which provides consistency and eliminates null-checking overhead.
Default values:
Empty DateTime →
DateTime.MinValueEmpty decimal →
0Empty Guid →
Guid.Empty
This approach is semantic: "Maximum 0" clearly means no maximum limit.
Don't Return Anonymous Objects
Never return anonymous objects with new { ... } in controllers or services. Anonymous objects are untyped, inconsistent, and break API contracts.
// Different types returned in different branches
if (ok)
return Ok(new { version = "2", revision = "2" });
else
return Ok(new { version = 2, revision = "2" }); // int vs stringpublic class AppInfoDTO
{
public string Version { get; set; } = "";
public string Revision { get; set; } = "";
public string Status { get; set; } = "";
}
return Ok(new AppInfoDTO
{
Version = "2",
Revision = "2",
Status = "OK"
});Don't Use ! (Negation Operator)
The negation operator reduces clarity, especially in long or nested conditions. Direct checks or explicit comparisons are clearer and more expressive.
// Correct
if (user.Enabled == false)
return;
// Incorrect
if (!user.Enabled)
return;Collections - Use Extension Methods
Prefer semantic extension methods over verbose null-conditional operators.
// Correct
if (_items.IsNotEmpty())
ProcessItems();
// Incorrect
if (_items?.Count > 0)
ProcessItems();Guid Comparison
Use semantic extension methods instead of direct comparison with Guid.Empty.
// Correct
if (f.NextRowId.IsEmpty())
return;
if (prevId.IsNotEmpty())
Process(prevId);
// Incorrect
if (f.NextRowId == Guid.Empty)
return;
if (prevId != Guid.Empty)
Process(prevId);Use Decimal for Numeric Types
Always use decimal for financial and logical operations. Float and double introduce binary rounding errors that cause precision issues.
// Correct
decimal price = 19.99m;
decimal total = price * quantity;
// Incorrect
float price = 19.99f;
double total = price * quantity;Extension Methods Reference
Type Conversion
.STR()- String conversion with better defaults than.ToString()
// Decimal - always US format (dot as decimal separator)
12.5m.STR() // "12.5"
1234.56m.STR() // "1234.56"
// Guid - empty guid returns empty string
Guid.Empty.STR() // ""
Guid.NewGuid().STR() // "a1b2c3d4-e5f6-..."
// DateTime - ISO format
DateTime.Now.STR() // "2024-01-15 14:30:00"
DateOnly.Today.STR() // "2024-01-15"
// Boolean - numeric representation
true.STR() // "1"
false.STR() // "0".INT([default])- Integer conversion
"123".INT() // 123
"abc".INT() // Exception
"abc".INT(99) // 99.DEC([default])- Decimal conversion
"12.5".DEC() // 12.5
"abc".DEC() // Exception
"abc".DEC(9.9m) // 9.9.BOOL()- Boolean conversion
"1".BOOL() // true
"si".BOOL() // true
"sí".BOOL() // true
"on".BOOL() // true
"yes".BOOL() // true
"true".BOOL() // true
"0".BOOL() // false
"false".BOOL() // false
// Any other value → falseValidation
.IsNotNull()- Checks if reference is not null.IsNull()- Checks if reference is null.IsEmpty()- Checks if value is empty:Guid.Empty""(empty string)DateTime.MinValue0for numeric typesCollections with zero elements
.IsNotEmpty()- Opposite of.IsEmpty(). For collections: returns true if at least one element exists.
These methods include built-in null checks and syntax highlighting.
String Comparison
.EqualsIgnoreCase()- Case-insensitive string comparison.LikeM()- LIKE-style wildcard pattern matching (case-sensitive).LikeMIgnoreCase()- LIKE-style wildcard pattern matching (case-insensitive)
Dictionary Operations
.GetM(key [, defaultValue])- Safe dictionary getter that never throws:
MyDictionary.GetM("key") // null if not found
MyDictionary.GetM("key", "default") // "default" if not foundUse .GetM() whenever it reduces lines of code.
Blazor
Component Properties
Only use quotes for string literals. Use @ for expressions to ensure type safety.
// Correct
<DataGrid [email protected] />
// Incorrect
<DataGrid Data="x.Data" />Última actualización