Eine Sammlung von meist angemessenen Richtlinien und Best Practices, um saubere, lesbare, verständliche und wartbare C# -Codes zu schreiben.
var , wann immer angemessen und möglichZweck: bessere Lesbarkeit und Sauberkeit
GUT
var httpClient = new HttpClient ( ) ;SCHLECHT
HttpClient httpClient = new HttpClient ( ) ; Zweck: bessere Lesbarkeit und Sauberkeit
GUT
var user = new User
{
Username = "admin" ,
Age = 31
} ;SCHLECHT
var user = new User ( ) ;
user . Username = "admin" ;
user . Age = 31 ; string.FormatZweck: Bessere Lesbarkeit und Semantik
GUT
var url = "http://localhost/api" ;
var resource = "users" ;
var path = $ " { url } / { resource } " ;SCHLECHT
var url = "http://localhost/api" ;
var resource = "users" ;
var path = string . Format ( "{0}/{1}" , url , resource ) ;Zweck: Keine Notwendigkeit, Backslash -Charaktere zu entkommen
GUT
var path = @"C:UsersAdministratorDocuments" ;SCHLECHT
var path = "C: \ Users \ Administrator \ Documents" ;Zweck: Refactoring kann ein Albtraum werden, wenn String -Literale mehrmals überall verteilt werden
GUT
const string error = "user_not_found" ;
Log . Error ( error ) ;
return BadRequest ( error ) ;SCHLECHT
Log . Error ( "user_not_found" ) ;
return BadRequest ( "user_not_found" ) ; xyz benennen. Es ist einfach lächerlich.Zweck: Verkürzung von Variablennamen fügt keinen Wert hinzu, sondern macht Code sogar viel schwerer zu lesen und zu verstehen
GUT
// Nice
var validationResult = validator . Validate ( ) ;
// Nice
var stringBuilder = new StringBuilder ( ) ;
// Nice
const string directorySeparator = "/" ;SCHLECHT
//
var res = validator . Validate ( ) ;
//
var sbd = new StringBuilder ( ) ;
// Seriously?
const string dsep = "/" ;Zweck: Der Code erleichtert das Lesen, Pflegen und Arbeiten enorm mit
GUT
// 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 )
{
}
}SCHLECHT
// 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 )
{
}
}Zweck: macht den Code unnötig länger und schwerer zu lesen und zu verstehen
GUT
// Clearly an interface
public interface IOrderManager { }
// Clearly a list of orders
private IList < Order > orders ;SCHLECHT
// Clearly an interface => no "Interface" postfix necessary
public interface IOrderManagerInterface { }
// Clearly a list of oders => no "List" postfix necessary
private IList < Order > orderList ;Zweck: Die Praxis zeigt, dass Kommentare sehr einfach und schnell veraltet werden können, wenn die Codebasis wächst und sich weiterentwickelt. Eine Self-Planatory-Code-Basis trägt zu einer besseren Wartbarkeit bei und verringert die Notwendigkeit, unnötige Kommentare zu schreiben und aufrechtzuerhalten. (Dies bedeutet nicht, dass das Schreiben von Kommentaren veraltet ist.)
GUT
public class OrderManager
{
public void ProcessOrder ( Order order )
{
var items = order . GetItems ( ) ;
foreach ( var item in items )
{
var availability = item . GetAvailability ( ) ;
}
}
}SCHLECHT
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 ( ) ;
}
}
}Zweck: In Übereinstimmung mit dem .NET -Framework und leichter zu lesen
GUT
public class JsonParser { }SCHLECHT
public class JSONParser { } Zweck: enum werden immer einzeln angesprochen. Zum Beispiel ist EmploymentType.FullTime EmploymentTypes.FullTime Darüber hinaus stellen Klassen immer eine einzelne Instanz eines Objekts dar, zum Beispiel ein einzelner User . Ausnahme: Bit Field Enums
GUT
public enum EmploymentType
{
FullTime ,
PartTime
}
public class User
{
}SCHLECHT
public enum EmploymentTypes
{
FullTime ,
PartTime
}
public class Users
{
} Zweck: Syntaktischer Zucker ¯ (ツ) /¯
GUT
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 ;
}
}SCHLECHT
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 ;
}
} Zweck: Besseres Bewusstsein dafür, was schief gelaufen ist. Ermöglicht mehr Präzision während der Protokollierung und Fehleranalyse.
GUT
try
{
System . IO . File . Open ( path ) ;
}
catch ( FileNotFoundException ex )
{
// Specific
}
catch ( IOException ex )
{
// Specific
}
catch ( Exception ex )
{
// Default
}SCHLECHT
try
{
System . IO . File . Open ( path ) ;
}
catch ( Exception ex )
{
// SOMETHING went wrong
}Exception aus Zweck: Sehr schlechte Praxis. Exception sollte nur vom .NET -Framework ausgelegt werden.
GUT
public void ProcessOrder ( Order order )
{
if ( order == null )
{
throw new ArgumentNullException ( nameof ( order ) ) ;
}
}SCHLECHT
public void ProcessOrder ( Order order )
{
if ( order == null )
{
throw new Exception ( "Order is null." ) ;
}
} Zweck: Bessere Lesbarkeit
GUT
private string _username ;SCHLECHT
private string mUsername__ ;
// OR
private string username ;
// OR
private string username_ ; Zweck: Bessere Lesbarkeit, spart Platz
GUT
public class JsonParser
{
private readonly string _json ;
public JsonParser ( string json )
{
_json = json ;
}
}SCHLECHT
public class JsonParser
{
private readonly string _json ;
public JsonParser ( string json )
{
_json = json ;
}
} Zweck: verhindert, dass Eigentum versehentlich von anderer Stelle geändert wird, eine bessere Vorhersage des Verhaltens der Anwendung
GUT
public class JsonParser
{
public string Json { get ; }
public JsonParser ( string json )
{
Json = json ;
}
}SCHLECHT
public class JsonParser
{
public string Json { get ; set ; }
public JsonParser ( string json )
{
Json = json ;
}
}Zweck: verhindert, dass die Sammlung von anderer Stelle geändert wird, bessere Vorhersage des Verhaltens der Anwendung
GUT
public class KeywordProvider
{
public IReadOnlyCollection < string > Keywords { get ; }
public KeywordProvider ( )
{
Keywords = new ReadOnlyCollection < string > ( new List < string >
{
"public" ,
"string"
} ) ;
}
}SCHLECHT
public class KeywordProvider
{
public IList < string > Keywords { get ; }
public KeywordProvider ( )
{
Keywords = new List < string >
{
"public" ,
"string"
} ;
}
} using Sie immer Blöcke Zweck: Die Ressource wird automatisch angezeigt, wenn der Block using wird
GUT
using ( var connection = new SqlConnection ( connectionString ) )
{
}SCHLECHT
try
{
var connection = new SqlConnection ( connectionString ) ;
}
finally
{
connection . Close ( ) ;
connection . Dispose ( ) ;
} Zweck: Bessere Lesbarkeit, einfachere Wartung, wenn eine andere Linie im Zustand erforderlich ist
GUT
if ( user . IsElevated )
{
// Do something
}SCHLECHT
if ( user . IsElevated )
// Do somethingZweck: Sie wissen, welcher Teil der Nachricht Variablen enthält.
GUT
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 ) ;
}SCHLECHT
var filePath = @"C:tmpmy-file.txt" ;
try
{
var file = File . Open ( filePath ) ;
}
catch ( Exception ex )
{
Log . Error ( $ "Could not open file { filePath } ." , ex ) ;
}Zweck: Damit die Ausnahme in bestimmten Fällen gefiltert werden kann (z. B. in der Benutzeroberfläche, da die gesamte Stapelverfolgung für den Benutzer nutzlos ist).
GUT
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 ) ;
}SCHLECHT
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 } " ) ;
}