Una colección de pautas y mejores prácticas en su mayoría razonables para escribir código C# limpio, legible, comprensible y mantenible.
var siempre que sea apropiado y posiblePropósito: mejor legibilidad y limpieza
BIEN
var httpClient = new HttpClient ( ) ;MALO
HttpClient httpClient = new HttpClient ( ) ; Propósito: mejor legibilidad y limpieza
BIEN
var user = new User
{
Username = "admin" ,
Age = 31
} ;MALO
var user = new User ( ) ;
user . Username = "admin" ;
user . Age = 31 ; string.FormatPropósito: mejor legibilidad y semántica
BIEN
var url = "http://localhost/api" ;
var resource = "users" ;
var path = $ " { url } / { resource } " ;MALO
var url = "http://localhost/api" ;
var resource = "users" ;
var path = string . Format ( "{0}/{1}" , url , resource ) ;Propósito: No es necesario escapar de los personajes de barra descarga
BIEN
var path = @"C:UsersAdministratorDocuments" ;MALO
var path = "C: \ Users \ Administrator \ Documents" ;Propósito: La refactorización puede convertirse en una pesadilla cuando los literales de cuerdas se distribuyen por todas partes varias veces
BIEN
const string error = "user_not_found" ;
Log . Error ( error ) ;
return BadRequest ( error ) ;MALO
Log . Error ( "user_not_found" ) ;
return BadRequest ( "user_not_found" ) ; xyz . Es simplemente ridículo.Propósito: Acortar los nombres de las variables no agrega ningún valor, incluso hace que el código sea mucho más difícil de leer y comprender
BIEN
// Nice
var validationResult = validator . Validate ( ) ;
// Nice
var stringBuilder = new StringBuilder ( ) ;
// Nice
const string directorySeparator = "/" ;MALO
//
var res = validator . Validate ( ) ;
//
var sbd = new StringBuilder ( ) ;
// Seriously?
const string dsep = "/" ;Propósito: hace que el código sea enormemente más fácil de leer, mantener y trabajar con
BIEN
// The purpose of this class can be easily inferred
public class OrderManager
{
// Using "Is" or "Has" as prefix clearly indicates that a method returns a boolean value
public bool IsFulfilled ( Order order )
{
}
public bool HasPositions ( Order order )
{
}
// Using a verb clearly indicates that a method performs some action
public void ProcessOrder ( Order order )
{
}
public void CancelOrder ( Order order )
{
}
}MALO
// Purpose of this class can not be easily inferred
public class OrderHelper
{
// Unclear
public bool Fulfilled ( Order order )
{
}
// Unclear => users would likely expect a method that retrieves the positions of the order due to the verb "Get"
public bool GetPositions ( Order order )
{
}
// Unclear
public void Order ( Order order )
{
}
// Unclear
public void StopOrder ( Order order )
{
}
}Propósito: hace que el código sea innecesariamente más largo y más difícil de leer y comprender
BIEN
// Clearly an interface
public interface IOrderManager { }
// Clearly a list of orders
private IList < Order > orders ;MALO
// Clearly an interface => no "Interface" postfix necessary
public interface IOrderManagerInterface { }
// Clearly a list of oders => no "List" postfix necessary
private IList < Order > orderList ;Propósito: La práctica muestra que los comentarios pueden estar desactualizados de manera muy fácil y rápida a medida que la base del código crece y evoluciona. Tener una base de código autoexplicativo contribuye a una mejor capacidad de mantenimiento y reduce la necesidad de escribir y mantener comentarios innecesarios. (Esto no significa que escribir comentarios sea obsoleto).
BIEN
public class OrderManager
{
public void ProcessOrder ( Order order )
{
var items = order . GetItems ( ) ;
foreach ( var item in items )
{
var availability = item . GetAvailability ( ) ;
}
}
}MALO
public class OrderManager
{
public void ExecuteOrder ( Order order )
{
// Get all available items from the order
var data = order . GetData ( ) ;
foreach ( var x in data )
{
// Determine the availability of the item
var available = item . CheckAvailability ( ) ;
}
}
}Propósito: De acuerdo con el marco .NET y más fácil de leer
BIEN
public class JsonParser { }MALO
public class JSONParser { } Propósito: enum s siempre se abordan individualmente. Por ejemplo, EmploymentType.FullTime es mucho más limpio que EmploymentTypes.FullTime . Además, las clases siempre representan una sola instancia de un objeto, por ejemplo, un User individual. Excepción: Bit Field Enums
BIEN
public enum EmploymentType
{
FullTime ,
PartTime
}
public class User
{
}MALO
public enum EmploymentTypes
{
FullTime ,
PartTime
}
public class Users
{
} Propósito: azúcar sintáctica ¯ (ツ) /¯
BIEN
public class User
{
public string Username { get ; }
public int Age { get ; }
public string DisplayName => $ " { Username } ( { Age } )" ;
public User ( string username , int age )
{
Username = username ;
Age = age ;
}
}MALO
public class User
{
public string Username { get ; }
public int Age { get ; }
public string DisplayName
{
get
{
return $ " { Username } ( { Age } )" ;
}
}
public User ( string username , int age )
{
Username = username ;
Age = age ;
}
} Propósito: Mejor conciencia de lo que salió mal. Permite más precisión durante el registro y el análisis de errores.
BIEN
try
{
System . IO . File . Open ( path ) ;
}
catch ( FileNotFoundException ex )
{
// Specific
}
catch ( IOException ex )
{
// Specific
}
catch ( Exception ex )
{
// Default
}MALO
try
{
System . IO . File . Open ( path ) ;
}
catch ( Exception ex )
{
// SOMETHING went wrong
}Exception usted mismo usted mismo Propósito: Muy mala práctica. Exception solo deben ser lanzadas por el marco .NET.
BIEN
public void ProcessOrder ( Order order )
{
if ( order == null )
{
throw new ArgumentNullException ( nameof ( order ) ) ;
}
}MALO
public void ProcessOrder ( Order order )
{
if ( order == null )
{
throw new Exception ( "Order is null." ) ;
}
} Propósito: mejor legibilidad
BIEN
private string _username ;MALO
private string mUsername__ ;
// OR
private string username ;
// OR
private string username_ ; Propósito: mejor legibilidad, ahorra espacio
BIEN
public class JsonParser
{
private readonly string _json ;
public JsonParser ( string json )
{
_json = json ;
}
}MALO
public class JsonParser
{
private readonly string _json ;
public JsonParser ( string json )
{
_json = json ;
}
} Propósito: evita que la propiedad se cambie accidentalmente desde otro lugar, una mejor predicción del comportamiento de la aplicación
BIEN
public class JsonParser
{
public string Json { get ; }
public JsonParser ( string json )
{
Json = json ;
}
}MALO
public class JsonParser
{
public string Json { get ; set ; }
public JsonParser ( string json )
{
Json = json ;
}
}Propósito: evita que la recolección se cambie de otro lugar, una mejor predicción del comportamiento de la aplicación
BIEN
public class KeywordProvider
{
public IReadOnlyCollection < string > Keywords { get ; }
public KeywordProvider ( )
{
Keywords = new ReadOnlyCollection < string > ( new List < string >
{
"public" ,
"string"
} ) ;
}
}MALO
public class KeywordProvider
{
public IList < string > Keywords { get ; }
public KeywordProvider ( )
{
Keywords = new List < string >
{
"public" ,
"string"
} ;
}
} using bloques Propósito: el recurso se elimina automáticamente cuando el bloque using termina
BIEN
using ( var connection = new SqlConnection ( connectionString ) )
{
}MALO
try
{
var connection = new SqlConnection ( connectionString ) ;
}
finally
{
connection . Close ( ) ;
connection . Dispose ( ) ;
} Propósito: mejor legibilidad, mantenimiento más fácil cuando se requiere otra línea dentro de la condición
BIEN
if ( user . IsElevated )
{
// Do something
}MALO
if ( user . IsElevated )
// Do somethingPropósito: para que sea consciente de qué parte del mensaje contiene variables.
BIEN
var filePath = @"C:tmpmy-file.txt" ;
try
{
var file = File . Open ( filePath ) ;
}
catch ( Exception ex )
{
// GOOD: Add [ ] to the variable
Log . Error ( $ "Could not open file [ { filePath } ]." , ex ) ;
}MALO
var filePath = @"C:tmpmy-file.txt" ;
try
{
var file = File . Open ( filePath ) ;
}
catch ( Exception ex )
{
Log . Error ( $ "Could not open file { filePath } ." , ex ) ;
}Propósito: para que la excepción se pueda filtrar en ciertos casos (por ejemplo, en la interfaz de usuario, ya que toda la traza de pila es inútil para el usuario).
BIEN
try
{
var file = File . Open ( @"C:tmpmy-file.txt" ) ;
}
catch ( Exception ex )
{
// Use appropriate signature of your logger to include the exception as separate parameter
Log . Error ( "Could not open file." , ex ) ;
}MALO
try
{
var file = File . Open ( @"C:tmpmy-file.txt" ) ;
}
catch ( Exception ex )
{
// Strictly AVOID this. The exception is added directly to the log message, which makes it impossible to filter the exception
Log . Error ( $ "Could not open file: { ex } " ) ;
}