Guías de estilo C#
Configuración de proyecto, cultura, valores centinela en vez de nulables y las extensiones de conversión y validación del SDK.
Las convenciones de C# que esperan el SDK Dinaup y sus extensiones. Aplícalas en tu app para que el código encaje con el SDK y con DinaZen.
Configuración del proyecto
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Using Include="Dinaup.extensions" Static="true" />
<Using Include="DemoUp.MyDinaup" />
</ItemGroup>Con Nullable deshabilitado el compilador no pide ? y los valores centinela son la opción por defecto. El Using estático de Dinaup.extensions deja .STR(), .IsEmpty() o .GetM() disponibles en todos los ficheros sin using por archivo.
Cultura y fechas
Las extensiones de conversión del SDK no leen la cultura del hilo: .STR() escribe los decimales con punto (12.5m.STR() da "12.5") y .DEC() lee el punto como separador decimal, salvo que le pases una CultureInfo. Un decimal viaja igual entre un servidor español y uno alemán.
Fija la cultura de la interfaz al arrancar la app en vez de heredar la del servidor, en Program.cs:
var cultureInfo = new CultureInfo("es-ES");
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.CurrentCulture = cultureInfo;Las fechas son UTC. Los campos de fecha de MyDinaup llevan el sufijo _UTC (FechaAlta_UTC) y los informes convierten con .ToDateTime_UTC(). Sin horas locales ni conversiones de zona en el modelo; la zona del usuario se aplica al mostrar los datos.
Prácticas
Sin tipos nulables
Evita DateTime?, decimal? o Guid? salvo casos contados. Cada tipo tiene su valor vacío:
| Tipo | Vacío |
|---|---|
DateTime | DateTime.MinValue |
decimal y numéricos | 0 ("Máximo 0" significa sin máximo) |
Guid | Guid.Empty (.STR() lo convierte en "") |
string | "" |
Sin objetos anónimos
No devuelvas new { ... } desde controladores ni servicios: cada rama devuelve un tipo distinto y rompe el contrato de la API. Usa una clase con sufijo DTO.
// Tipos distintos en cada rama
if (ok)
return Ok(new { version = "2", revision = "2" });
else
return Ok(new { version = 2, revision = "2" }); // int frente a 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"
});Colecciones y Guid con extensiones
// Correcto
if (_items.IsNotEmpty())
ProcessItems();
if (f.NextRowId.IsEmpty())
return;
// Incorrecto
if (_items?.Count > 0)
ProcessItems();
if (f.NextRowId == Guid.Empty)
return;.IsEmpty() e .IsNotEmpty() son un par: nunca niegues uno teniendo el otro. lista.IsEmpty(), no lista.IsNotEmpty() == false.
Decimal para importes
float y double introducen errores de redondeo binario. Los importes y los cálculos de negocio van en decimal.
// Correcto
decimal price = 19.99m;
decimal total = price * quantity;
// Incorrecto
float price = 19.99f;
double total = price * quantity;Extensiones de conversión
| Extensión | Qué hace |
|---|---|
.STR() | Texto con mejores valores por defecto que .ToString(): decimal con punto y sin ceros sobrantes, Guid.Empty y nulos a "", bool a "1" o "0", DateTime a yyyy-MM-dd HH:mm:ss, DateOnly a yyyy-MM-dd. |
.INT() | string a int. Sin valor por defecto lanza excepción si no es un número. |
.INT(defecto) | Devuelve defecto si no puede convertir. |
.DEC() | string a decimal con punto decimal; quita € y $. Lanza Exception("Invalid cast") si no puede. |
.DEC(defecto) | Devuelve defecto si no puede convertir. |
.BOOL() | "1", "si", "sí", "on", "yes" y "true" son true (sin distinguir mayúsculas). Cualquier otro valor es false. |
12.5m.STR() // "12.5"
Guid.Empty.STR() // ""
true.STR() // "1"
DateTime.Now.STR() // "2026-09-05 14:30:00"
"123".INT() // 123
"abc".INT() // excepción
"abc".INT(99) // 99
"12.5".DEC() // 12.5
"abc".DEC(9.9m) // 9.9
"si".BOOL() // true
"0".BOOL() // falseExtensiones de validación
| Extensión | Qué comprueba |
|---|---|
.IsEmpty() | "" o null en string; Guid.Empty en Guid y Guid?; nulo o MinValue en DateTime?, DateOnly? y TimeOnly?; sin elementos en List, IEnumerable, arrays, Dictionary, IList e IDictionary; Id vacío en DinaupBasicInformation e IDinaupRow. |
.IsNotEmpty() | Lo contrario. Para colecciones, al menos un elemento. |
.IsNull() / .IsNotNull() | Solo la referencia. |
.EqualsIgnoreCase(otro) | Igualdad de string sin distinguir mayúsculas. |
.LikeM(a, b, c) | true si el valor es igual a alguna de las opciones. Para string distingue mayúsculas; también existe para int, decimal, Guid y char. |
.LikeMIgnoreCase(a, b, c) | Igual que LikeM para string, sin distinguir mayúsculas. |
.IsEmpty() no existe para int ni decimal: compara con 0.
if (estado.LikeM("Abierta", "Pagada"))
Process();
if (codigo.LikeM(200, 201, 204))
return;Diccionarios
.GetM(clave) devuelve null (o el valor por defecto del tipo) si la clave no está; .GetM(clave, defecto) devuelve defecto. Ninguno lanza.
dic.GetM("key") // null si no está
dic.GetM("key", "default") // "default" si no estáUsa .GetM() siempre que ahorre líneas.
Blazor
En los componentes, las comillas son solo para literales string. Las expresiones van con @, sin comillas, para que el compilador compruebe el tipo.
@* Correcto *@
<DnzReportView Client=@Client ReportId="83fd57a3-893c-4651-adf9-d5d980af2c85" />
@* Incorrecto *@
<DnzReportView Client="@Client" />Las convenciones para consumir MyDinaup están en C# con Dinaup y los componentes, en DinaZen.