如果您喜欢clean-code-dotnet项目,或者如果对您有所帮助,请为此存储库提供一颗星。这不仅将有助于加强我们的.NET社区,而且还可以提高世界各地.NET开发人员的清洁代码技能。非常感谢 ?
查看我的博客或在Twitter上打招呼!
罗伯特·马丁( Robert C.这不是样式指南。它是在.NET/.NET Core中生产可读,可重复使用和可重构软件的指南。
并非必须严格遵循此处的所有原则,甚至更少会被普遍同意。这些是准则,仅此而已,但是它们是在多年的集体经验中由清洁代码的集体经验编纂的。
灵感来自清洁代码javaScript和Clean-ode-PHP列表。
坏的:
int d ;好的:
int daySinceModification ;⬆回到顶部
命名变量以反映其使用的用途。
坏的:
var dataFromDb = db . GetFromService ( ) . ToList ( ) ;好的:
var listOfEmployee = _employeeService . GetEmployees ( ) . ToList ( ) ;⬆回到顶部
匈牙利符号重述了声明中已经存在的类型。这是毫无意义的,因为现代IDE将识别类型。
坏的:
int iCounter ;
string strFullName ;
DateTime dModifiedDate ;好的:
int counter ;
string fullName ;
DateTime modifiedDate ;匈牙利符号也不应在参数术中使用。
坏的:
public bool IsShopOpen ( string pDay , int pAmount )
{
// some logic
}好的:
public bool IsShopOpen ( string day , int amount )
{
// some logic
}⬆回到顶部
资本化告诉您有关您的变量,功能等的很多信息。这些规则是主观的,因此您的团队可以选择他们想要的任何东西。关键是,无论您选择什么,都保持一致。
坏的:
const int DAYS_IN_WEEK = 7 ;
const int daysInMonth = 30 ;
var songs = new List < string > { 'Back In Black' , 'Stairway to Heaven' , 'Hey Jude' } ;
var Artists = new List < string > { 'ACDC' , 'Led Zeppelin' , 'The Beatles' } ;
bool EraseDatabase ( ) { }
bool Restore_database ( ) { }
class animal { }
class Alpaca { }好的:
const int DaysInWeek = 7 ;
const int DaysInMonth = 30 ;
var songs = new List < string > { 'Back In Black' , 'Stairway to Heaven' , 'Hey Jude' } ;
var artists = new List < string > { 'ACDC' , 'Led Zeppelin' , 'The Beatles' } ;
bool EraseDatabase ( ) { }
bool RestoreDatabase ( ) { }
class Animal { }
class Alpaca { }⬆回到顶部
当变量不可发音时,研究变量和函数的含义将需要时间。
坏的:
public class Employee
{
public Datetime sWorkDate { get ; set ; } // what the heck is this
public Datetime modTime { get ; set ; } // same here
}好的:
public class Employee
{
public Datetime StartWorkingDate { get ; set ; }
public Datetime ModificationTime { get ; set ; }
}⬆回到顶部
使用骆驼箱表示法进行可变和方法参数。
坏的:
var employeephone ;
public double CalculateSalary ( int workingdays , int workinghours )
{
// some logic
}好的:
var employeePhone ;
public double CalculateSalary ( int workingDays , int workingHours )
{
// some logic
}⬆回到顶部
阅读您的代码的人也是程序员。正确命名的事情将帮助每个人在同一页面上。我们不想花时间向所有人解释变量或函数的目的。
好的
public class SingleObject
{
// create an object of SingleObject
private static SingleObject _instance = new SingleObject ( ) ;
// make the constructor private so that this class cannot be instantiated
private SingleObject ( ) { }
// get the only object available
public static SingleObject GetInstance ( )
{
return _instance ;
}
public string ShowMessage ( )
{
return "Hello World!" ;
}
}
public static void main ( String [ ] args )
{
// illegal construct
// var object = new SingleObject();
// Get the only object available
var singletonObject = SingleObject . GetInstance ( ) ;
// show the message
singletonObject . ShowMessage ( ) ;
}⬆回到顶部
如果其他语句可以使代码难以遵循,太多了。明确胜于隐式。
坏的:
public bool IsShopOpen ( string day )
{
if ( ! string . IsNullOrEmpty ( day ) )
{
day = day . ToLower ( ) ;
if ( day == "friday" )
{
return true ;
}
else if ( day == "saturday" )
{
return true ;
}
else if ( day == "sunday" )
{
return true ;
}
else
{
return false ;
}
}
else
{
return false ;
}
}好的:
public bool IsShopOpen ( string day )
{
if ( string . IsNullOrEmpty ( day ) )
{
return false ;
}
var openingDays = new [ ] { "friday" , "saturday" , "sunday" } ;
return openingDays . Any ( d => d == day . ToLower ( ) ) ;
}坏的:
public long Fibonacci ( int n )
{
if ( n < 50 )
{
if ( n != 0 )
{
if ( n != 1 )
{
return Fibonacci ( n - 1 ) + Fibonacci ( n - 2 ) ;
}
else
{
return 1 ;
}
}
else
{
return 0 ;
}
}
else
{
throw new System . Exception ( "Not supported" ) ;
}
}好的:
public long Fibonacci ( int n )
{
if ( n == 0 )
{
return 0 ;
}
if ( n == 1 )
{
return 1 ;
}
if ( n > 50 )
{
throw new System . Exception ( "Not supported" ) ;
}
return Fibonacci ( n - 1 ) + Fibonacci ( n - 2 ) ;
}⬆回到顶部
不要强迫代码的读者转换变量的含义。明确胜于隐式。
坏的:
var l = new [ ] { "Austin" , "New York" , "San Francisco" } ;
for ( var i = 0 ; i < l . Count ( ) ; i ++ )
{
var li = l [ i ] ;
DoStuff ( ) ;
DoSomeOtherStuff ( ) ;
// ...
// ...
// ...
// Wait, what is `li` for again?
Dispatch ( li ) ;
}好的:
var locations = new [ ] { "Austin" , "New York" , "San Francisco" } ;
foreach ( var location in locations )
{
DoStuff ( ) ;
DoSomeOtherStuff ( ) ;
// ...
// ...
// ...
Dispatch ( location ) ;
}⬆回到顶部
魔术字符串是直接在应用程序代码中指定的字符串值,对应用程序的行为产生影响。通常,这样的字符串最终会在系统中重复,并且由于无法使用重构工具自动更新它们,因此当对某些字符串进行更改而不是其他字符串进行更改时,它们成为常见的错误来源。
坏的
if ( userRole == "Admin" )
{
// logic in here
}好的
const string ADMIN_ROLE = "Admin"
if ( userRole == ADMIN_ROLE )
{
// logic in here
}使用此功能,我们只需要在集中化的地方进行更改,而其他人则将对其进行调整。
⬆回到顶部
如果您的类/对象名称告诉您一些事情,请不要在变量名称中重复此操作。
坏的:
public class Car
{
public string CarMake { get ; set ; }
public string CarModel { get ; set ; }
public string CarColor { get ; set ; }
//...
}好的:
public class Car
{
public string Make { get ; set ; }
public string Model { get ; set ; }
public string Color { get ; set ; }
//...
}⬆回到顶部
坏的:
var ymdstr = DateTime . UtcNow . ToString ( "MMMM dd, yyyy" ) ;好的:
var currentDate = DateTime . UtcNow . ToString ( "MMMM dd, yyyy" ) ;⬆回到顶部
坏的:
GetUserInfo ( ) ;
GetUserData ( ) ;
GetUserRecord ( ) ;
GetUserProfile ( ) ;好的:
GetUser ( ) ;⬆回到顶部
我们将读取的代码超出我们将要写的代码。重要的是,我们编写的代码是可以读取且可搜索的。通过不命名最终对于理解我们的计划有意义的变量,我们伤害了读者。使您的名字可搜索。
坏的:
// What the heck is data for?
var data = new { Name = "John" , Age = 42 } ;
var stream1 = new MemoryStream ( ) ;
var ser1 = new DataContractJsonSerializer ( typeof ( object ) ) ;
ser1 . WriteObject ( stream1 , data ) ;
stream1 . Position = 0 ;
var sr1 = new StreamReader ( stream1 ) ;
Console . Write ( "JSON form of Data object: " ) ;
Console . WriteLine ( sr1 . ReadToEnd ( ) ) ;好的:
var person = new Person
{
Name = "John" ,
Age = 42
} ;
var stream2 = new MemoryStream ( ) ;
var ser2 = new DataContractJsonSerializer ( typeof ( Person ) ) ;
ser2 . WriteObject ( stream2 , data ) ;
stream2 . Position = 0 ;
var sr2 = new StreamReader ( stream2 ) ;
Console . Write ( "JSON form of Data object: " ) ;
Console . WriteLine ( sr2 . ReadToEnd ( ) ) ;⬆回到顶部
坏的:
var data = new { Name = "John" , Age = 42 , PersonAccess = 4 } ;
// What the heck is 4 for?
if ( data . PersonAccess == 4 )
{
// do edit ...
}好的:
public enum PersonAccess : int
{
ACCESS_READ = 1 ,
ACCESS_CREATE = 2 ,
ACCESS_UPDATE = 4 ,
ACCESS_DELETE = 8
}
var person = new Person
{
Name = "John" ,
Age = 42 ,
PersonAccess = PersonAccess . ACCESS_CREATE
} ;
if ( person . PersonAccess == PersonAccess . ACCESS_UPDATE )
{
// do edit ...
}⬆回到顶部
坏的:
const string Address = "One Infinite Loop, Cupertino 95014" ;
var cityZipCodeRegex = @"/^[^,]+[,\s]+(.+?)s*(d{5})?$/" ;
var matches = Regex . Matches ( Address , cityZipCodeRegex ) ;
if ( matches [ 0 ] . Success == true && matches [ 1 ] . Success == true )
{
SaveCityZipCode ( matches [ 0 ] . Value , matches [ 1 ] . Value ) ;
}好的:
通过命名子图案降低对正则对等级的依赖。
const string Address = "One Infinite Loop, Cupertino 95014" ;
var cityZipCodeWithGroupRegex = @"/^[^,]+[,\s]+(?<city>.+?)s*(?<zipCode>d{5})?$/" ;
var matchesWithGroup = Regex . Match ( Address , cityZipCodeWithGroupRegex ) ;
var cityGroup = matchesWithGroup . Groups [ "city" ] ;
var zipCodeGroup = matchesWithGroup . Groups [ "zipCode" ] ;
if ( cityGroup . Success == true && zipCodeGroup . Success == true )
{
SaveCityZipCode ( cityGroup . Value , zipCodeGroup . Value ) ;
}⬆回到顶部
不好:
这不是很好,因为breweryName可能为NULL 。
该意见比以前的版本更容易理解,但是它可以更好地控制变量的价值。
public void CreateMicrobrewery ( string name = null )
{
var breweryName = ! string . IsNullOrEmpty ( name ) ? name : "Hipster Brew Co." ;
// ...
}好的:
public void CreateMicrobrewery ( string breweryName = "Hipster Brew Co." )
{
// ...
}⬆回到顶部
如果函数在占据值并返回其他值或值之外,则会产生副作用。副作用可能是写入文件,修改某些全局变量,或者将所有资金意外地接线到陌生人。
现在,您有时需要在程序中产生副作用。像上一个示例一样,您可能需要写入文件。您想做的是集中您在哪里做。没有写入特定文件的几个功能和类。有一项服务。一个也是一个。
要点是避免使用可以用任何结构的物体之间的对象之间共享状态,使用任何可以写入任何事物的可变数据类型,而不是集中副作用发生的位置。如果您可以做到这一点,那么您将比其他绝大多数程序员更快乐。
坏的:
// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
var name = "Ryan McDermott" ;
public void SplitAndEnrichFullName ( )
{
var temp = name . Split ( " " ) ;
name = $ "His first name is { temp [ 0 ] } , and his last name is { temp [ 1 ] } " ; // side effect
}
SplitAndEnrichFullName ( ) ;
Console . WriteLine ( name ) ; // His first name is Ryan, and his last name is McDermott好的:
public string SplitAndEnrichFullName ( string name )
{
var temp = name . Split ( " " ) ;
return $ "His first name is { temp [ 0 ] } , and his last name is { temp [ 1 ] } " ;
}
var name = "Ryan McDermott" ;
var fullName = SplitAndEnrichFullName ( name ) ;
Console . WriteLine ( name ) ; // Ryan McDermott
Console . WriteLine ( fullName ) ; // His first name is Ryan, and his last name is McDermott⬆回到顶部
坏的:
public bool IsDOMNodeNotPresent ( string node )
{
// ...
}
if ( ! IsDOMNodeNotPresent ( node ) )
{
// ...
}好的:
public bool IsDOMNodePresent ( string node )
{
// ...
}
if ( IsDOMNodePresent ( node ) )
{
// ...
}⬆回到顶部
这似乎是一项不可能的任务。首次听到此消息后,大多数人说:“ if没有陈述,我应该怎么做什么?”答案是您可以使用多态性在许多情况下完成相同的任务。第二个问题通常是:“那太好了,但是我为什么要这样做呢?”答案是我们学到的以前的干净代码概念:函数只能做一件事。当您具有if语句的类和功能时,您告诉用户您的功能不止一件事。记住,只做一件事。
坏的:
class Airplane
{
// ...
public double GetCruisingAltitude ( )
{
switch ( _type )
{
case '7 77 ' :
return GetMaxAltitude ( ) - GetPassengerCount ( ) ;
case 'Air Force One' :
return GetMaxAltitude ( ) ;
case 'Cessna' :
return GetMaxAltitude ( ) - GetFuelExpenditure ( ) ;
}
}
}好的:
interface IAirplane
{
// ...
double GetCruisingAltitude ( ) ;
}
class Boeing777 : IAirplane
{
// ...
public double GetCruisingAltitude ( )
{
return GetMaxAltitude ( ) - GetPassengerCount ( ) ;
}
}
class AirForceOne : IAirplane
{
// ...
public double GetCruisingAltitude ( )
{
return GetMaxAltitude ( ) ;
}
}
class Cessna : IAirplane
{
// ...
public double GetCruisingAltitude ( )
{
return GetMaxAltitude ( ) - GetFuelExpenditure ( ) ;
}
}⬆回到顶部
坏的:
public Path TravelToTexas ( object vehicle )
{
if ( vehicle . GetType ( ) == typeof ( Bicycle ) )
{
( vehicle as Bicycle ) . PeddleTo ( new Location ( "texas" ) ) ;
}
else if ( vehicle . GetType ( ) == typeof ( Car ) )
{
( vehicle as Car ) . DriveTo ( new Location ( "texas" ) ) ;
}
}好的:
public Path TravelToTexas ( Traveler vehicle )
{
vehicle . TravelTo ( new Location ( "texas" ) ) ;
}或者
// pattern matching
public Path TravelToTexas ( object vehicle )
{
if ( vehicle is Bicycle bicycle )
{
bicycle . PeddleTo ( new Location ( "texas" ) ) ;
}
else if ( vehicle is Car car )
{
car . DriveTo ( new Location ( "texas" ) ) ;
}
}⬆回到顶部
坏的:
public int Combine ( dynamic val1 , dynamic val2 )
{
int value ;
if ( ! int . TryParse ( val1 , out value ) || ! int . TryParse ( val2 , out value ) )
{
throw new Exception ( 'Must be of type Number' ) ;
}
return val1 + val2 ;
}好的:
public int Combine ( int val1 , int val2 )
{
return val1 + val2 ;
}⬆回到顶部
标志表明该方法具有多个责任。最好只有该方法只有一个责任。如果布尔参数将多个职责添加到该方法中,则将方法分为两个。
坏的:
public void CreateFile ( string name , bool temp = false )
{
if ( temp )
{
Touch ( "./temp/" + name ) ;
}
else
{
Touch ( name ) ;
}
}好的:
public void CreateFile ( string name )
{
Touch ( name ) ;
}
public void CreateTempFile ( string name )
{
Touch ( "./temp/" + name ) ;
}⬆回到顶部
在多种语言中,污染全球群体是一种不好的做法,因为您可以与另一个图书馆发生冲突,并且API的用户在生产中例外之前是不是翼的。让我们考虑一个例子:如果您想拥有配置数组,该怎么办。您可以像Config()一样编写全局函数,但是它可以与另一个尝试执行同一操作的库发生冲突。
坏的:
public Dictionary < string , string > Config ( )
{
return new Dictionary < string , string > ( ) {
[ "foo" ] = "bar"
} ;
}好的:
class Configuration
{
private Dictionary < string , string > _configuration ;
public Configuration ( Dictionary < string , string > configuration )
{
_configuration = configuration ;
}
public string [ ] Get ( string key )
{
return _configuration . ContainsKey ( key ) ? _configuration [ key ] : null ;
}
}加载配置并创建Configuration类的实例
var configuration = new Configuration ( new Dictionary < string , string > ( ) {
[ "foo" ] = "bar"
} ) ;现在,您必须在应用程序中使用Configuration实例。
⬆回到顶部
辛格尔顿是一个反模式。从Brian Button解释:
Misko Hevery关于问题的根源也有很好的想法。
坏的:
class DBConnection
{
private static DBConnection _instance ;
private DBConnection ( )
{
// ...
}
public static GetInstance ( )
{
if ( _instance == null )
{
_instance = new DBConnection ( ) ;
}
return _instance ;
}
// ...
}
var singleton = DBConnection . GetInstance ( ) ;好的:
class DBConnection
{
public DBConnection ( IOptions < DbConnectionOption > options )
{
// ...
}
// ...
}创建DBConnection类的实例,并使用选项模式进行配置。
var options = < resolve from IOC > ;
var connection = new DBConnection ( options ) ;现在,您必须在应用程序中使用DBConnection实例。
⬆回到顶部
限制功能参数的量非常重要,因为它使测试功能更容易。拥有三个以上的导致组合爆炸,您必须在每个单独的参数中测试大量不同的情况。
零论点是理想的情况。可以避免一个或两个参数,应避免三个论点。除此之外的任何事情都应该合并。通常,如果您有两个以上的参数,那么您的功能正在尝试做太多。如果不是这样,大多数情况下,更高级别的对象就足够了。
坏的:
public void CreateMenu ( string title , string body , string buttonText , bool cancellable )
{
// ...
}好的:
public class MenuConfig
{
public string Title { get ; set ; }
public string Body { get ; set ; }
public string ButtonText { get ; set ; }
public bool Cancellable { get ; set ; }
}
var config = new MenuConfig
{
Title = "Foo" ,
Body = "Bar" ,
ButtonText = "Baz" ,
Cancellable = true
} ;
public void CreateMenu ( MenuConfig config )
{
// ...
}⬆回到顶部
这是迄今为止软件工程中最重要的规则。当功能做不止一件事时,它们很难构成,测试和理由。当您只能将功能隔离为一个操作时,它们可以轻松进行重构,并且您的代码将读取更清洁。如果您除了本指南以外什么都不带走,那么您将领先于许多开发人员。
坏的:
public void SendEmailToListOfClients ( string [ ] clients )
{
foreach ( var client in clients )
{
var clientRecord = db . Find ( client ) ;
if ( clientRecord . IsActive ( ) )
{
Email ( client ) ;
}
}
}好的:
public void SendEmailToListOfClients ( string [ ] clients )
{
var activeClients = GetActiveClients ( clients ) ;
// Do some logic
}
public List < Client > GetActiveClients ( string [ ] clients )
{
return db . Find ( clients ) . Where ( s => s . Status == "Active" ) ;
}⬆回到顶部
坏的:
public class Email
{
//...
public void Handle ( )
{
SendMail ( this . _to , this . _subject , this . _body ) ;
}
}
var message = new Email ( .. . ) ;
// What is this? A handle for the message? Are we writing to a file now?
message . Handle ( ) ;好的:
public class Email
{
//...
public void Send ( )
{
SendMail ( this . _to , this . _subject , this . _body ) ;
}
}
var message = new Email ( .. . ) ;
// Clear and obvious
message . Send ( ) ;⬆回到顶部
尚未完成
当您拥有多个级别的抽象时,您的功能通常会做得太多。分开功能会导致可重复使用和更容易的测试。
坏的:
public string ParseBetterJSAlternative ( string code )
{
var regexes = [
// ...
] ;
var statements = explode ( " " , code ) ;
var tokens = new string [ ] { } ;
foreach ( var regex in regexes )
{
foreach ( var statement in statements )
{
// ...
}
}
var ast = new string [ ] { } ;
foreach ( var token in tokens )
{
// lex...
}
foreach ( var node in ast )
{
// parse...
}
}也很糟糕:
我们已经执行了一些功能,但是ParseBetterJSAlternative()函数仍然非常复杂,无法测试。
public string Tokenize ( string code )
{
var regexes = new string [ ]
{
// ...
} ;
var statements = explode ( " " , code ) ;
var tokens = new string [ ] { } ;
foreach ( var regex in regexes )
{
foreach ( var statement in statements )
{
tokens [ ] = /* ... */ ;
}
}
return tokens ;
}
public string Lexer ( string [ ] tokens )
{
var ast = new string [ ] { } ;
foreach ( var token in tokens )
{
ast [ ] = /* ... */ ;
}
return ast ;
}
public string ParseBetterJSAlternative ( string code )
{
var tokens = Tokenize ( code ) ;
var ast = Lexer ( tokens ) ;
foreach ( var node in ast )
{
// parse...
}
}好的:
最好的解决方案是移出ParseBetterJSAlternative()函数的依赖关系。
class Tokenizer
{
public string Tokenize ( string code )
{
var regexes = new string [ ] {
// ...
} ;
var statements = explode ( " " , code ) ;
var tokens = new string [ ] { } ;
foreach ( var regex in regexes )
{
foreach ( var statement in statements )
{
tokens [ ] = /* ... */ ;
}
}
return tokens ;
}
}
class Lexer
{
public string Lexify ( string [ ] tokens )
{
var ast = new [ ] { } ;
foreach ( var token in tokens )
{
ast [ ] = /* ... */ ;
}
return ast ;
}
}
class BetterJSAlternative
{
private string _tokenizer ;
private string _lexer ;
public BetterJSAlternative ( Tokenizer tokenizer , Lexer lexer )
{
_tokenizer = tokenizer ;
_lexer = lexer ;
}
public string Parse ( string code )
{
var tokens = _tokenizer . Tokenize ( code ) ;
var ast = _lexer . Lexify ( tokens ) ;
foreach ( var node in ast )
{
// parse...
}
}
}⬆回到顶部
如果函数调用另一个函数,请在源文件中垂直垂直关闭这些函数。理想情况下,将呼叫者保持在卡利上方。我们倾向于像报纸一样从上到下阅读代码。因此,使您的代码读取方式。
坏的:
class PerformanceReview
{
private readonly Employee _employee ;
public PerformanceReview ( Employee employee )
{
_employee = employee ;
}
private IEnumerable < PeersData > LookupPeers ( )
{
return db . lookup ( _employee , 'peers' ) ;
}
private ManagerData LookupManager ( )
{
return db . lookup ( _employee , 'manager' ) ;
}
private IEnumerable < PeerReviews > GetPeerReviews ( )
{
var peers = LookupPeers ( ) ;
// ...
}
public PerfReviewData PerfReview ( )
{
GetPeerReviews ( ) ;
GetManagerReview ( ) ;
GetSelfReview ( ) ;
}
public ManagerData GetManagerReview ( )
{
var manager = LookupManager ( ) ;
}
public EmployeeData GetSelfReview ( )
{
// ...
}
}
var review = new PerformanceReview ( employee ) ;
review . PerfReview ( ) ;好的:
class PerformanceReview
{
private readonly Employee _employee ;
public PerformanceReview ( Employee employee )
{
_employee = employee ;
}
public PerfReviewData PerfReview ( )
{
GetPeerReviews ( ) ;
GetManagerReview ( ) ;
GetSelfReview ( ) ;
}
private IEnumerable < PeerReviews > GetPeerReviews ( )
{
var peers = LookupPeers ( ) ;
// ...
}
private IEnumerable < PeersData > LookupPeers ( )
{
return db . lookup ( _employee , 'peers' ) ;
}
private ManagerData GetManagerReview ( )
{
var manager = LookupManager ( ) ;
return manager ;
}
private ManagerData LookupManager ( )
{
return db . lookup ( _employee , 'manager' ) ;
}
private EmployeeData GetSelfReview ( )
{
// ...
}
}
var review = new PerformanceReview ( employee ) ;
review . PerfReview ( ) ;⬆回到顶部
坏的:
if ( article . state == "published" )
{
// ...
}好的:
if ( article . IsPublished ( ) )
{
// ...
}⬆回到顶部
死代码与重复代码一样糟糕。没有理由将其保存在您的代码库中。如果没有被称为,请摆脱它!如果您仍然需要它,它在您的版本历史记录中仍然是安全的。
坏的:
public void OldRequestModule ( string url )
{
// ...
}
public void NewRequestModule ( string url )
{
// ...
}
var request = NewRequestModule ( requestUrl ) ;
InventoryTracker ( "apples" , request , "www.inventory-awesome.io" ) ;好的:
public void RequestModule ( string url )
{
// ...
}
var request = RequestModule ( requestUrl ) ;
InventoryTracker ( "apples" , request , "www.inventory-awesome.io" ) ;⬆回到顶部
在C# / vb.net中,您可以为方法设置public , protected和private关键字。使用它,您可以控制对象上的属性修改。
set时使添加验证简单。此外,这是从面向对象的设计原理中的开放/封闭原则的一部分。
坏的:
class BankAccount
{
public double Balance = 1000 ;
}
var bankAccount = new BankAccount ( ) ;
// Fake buy shoes...
bankAccount . Balance -= 100 ;好的:
class BankAccount
{
private double _balance = 0.0D ;
pubic double Balance {
get {
return _balance ;
}
}
public BankAccount ( balance = 1000 )
{
_balance = balance ;
}
public void WithdrawBalance ( int amount )
{
if ( amount > _balance )
{
throw new Exception ( 'Amount greater than available balance . ' ) ;
}
_balance -= amount ;
}
public void DepositBalance ( int amount )
{
_balance += amount ;
}
}
var bankAccount = new BankAccount ( ) ;
// Buy shoes...
bankAccount . WithdrawBalance ( price ) ;
// Get balance
balance = bankAccount . Balance ;⬆回到顶部
坏的:
class Employee
{
public string Name { get ; set ; }
public Employee ( string name )
{
Name = name ;
}
}
var employee = new Employee ( "John Doe" ) ;
Console . WriteLine ( employee . Name ) ; // Employee name: John Doe好的:
class Employee
{
public string Name { get ; }
public Employee ( string name )
{
Name = name ;
}
}
var employee = new Employee ( "John Doe" ) ;
Console . WriteLine ( employee . Name ) ; // Employee name: John Doe⬆回到顶部
这种模式非常有用,并且在许多库中常用。它允许您的代码具有表现力,更少的详细信息。因此,使用方法链条并查看代码的清洁程度。
好的:
public static class ListExtensions
{
public static List < T > FluentAdd < T > ( this List < T > list , T item )
{
list . Add ( item ) ;
return list ;
}
public static List < T > FluentClear < T > ( this List < T > list )
{
list . Clear ( ) ;
return list ;
}
public static List < T > FluentForEach < T > ( this List < T > list , Action < T > action )
{
list . ForEach ( action ) ;
return list ;
}
public static List < T > FluentInsert < T > ( this List < T > list , int index , T item )
{
list . Insert ( index , item ) ;
return list ;
}
public static List < T > FluentRemoveAt < T > ( this List < T > list , int index )
{
list . RemoveAt ( index ) ;
return list ;
}
public static List < T > FluentReverse < T > ( this List < T > list )
{
list . Reverse ( ) ;
return list ;
}
}
internal static void ListFluentExtensions ( )
{
var list = new List < int > ( ) { 1 , 2 , 3 , 4 , 5 }
. FluentAdd ( 1 )
. FluentInsert ( 0 , 0 )
. FluentRemoveAt ( 1 )
. FluentReverse ( )
. FluentForEach ( value => value . WriteLine ( ) )
. FluentClear ( ) ;
}⬆回到顶部
正如四个团伙在设计模式中著名的那样,您应该更喜欢构图而不是继承。有很多充分的理由使用继承和许多充分的理由使用构图。
这一格言的要点是,如果您的思想本能地进行继承,请尝试思考构图是否可以更好地建模您的问题。在某些情况下可以。
然后,您可能想知道:“我什么时候应该使用继承?”这取决于您手头的问题,但这是继承何时比组成更有意义的列表:
坏的:
class Employee
{
private string Name { get ; set ; }
private string Email { get ; set ; }
public Employee ( string name , string email )
{
Name = name ;
Email = email ;
}
// ...
}
// Bad because Employees "have" tax data.
// EmployeeTaxData is not a type of Employee
class EmployeeTaxData : Employee
{
private string Name { get ; }
private string Email { get ; }
public EmployeeTaxData ( string name , string email , string ssn , string salary )
{
// ...
}
// ...
}好的:
class EmployeeTaxData
{
public string Ssn { get ; }
public string Salary { get ; }
public EmployeeTaxData ( string ssn , string salary )
{
Ssn = ssn ;
Salary = salary ;
}
// ...
}
class Employee
{
public string Name { get ; }
public string Email { get ; }
public EmployeeTaxData TaxData { get ; }
public Employee ( string name , string email )
{
Name = name ;
Email = email ;
}
public void SetTax ( string ssn , double salary )
{
TaxData = new EmployeeTaxData ( ssn , salary ) ;
}
// ...
}⬆回到顶部
Solid是Michael Feathers为Robert Martin命名的前五个原则提出的助记符首字母缩写,该原则是面向对象的编程和设计的五个基本原则。
如干净的代码中所述,“不应该有一个班级更改的理由”。很容易用很多功能挤压一堂课,例如,您只能在飞行中乘坐一个手提箱。这样做的问题是,您的班级在概念上不会具有凝聚力,它将给它带来许多改变的理由。最大程度地减少您需要更改课程的次数很重要。
这很重要,因为如果在一个类中过多功能并修改其中的一部分,那么很难理解这将如何影响代码库中其他依赖模块。
坏的:
class UserSettings
{
private User User ;
public UserSettings ( User user )
{
User = user ;
}
public void ChangeSettings ( Settings settings )
{
if ( verifyCredentials ( ) )
{
// ...
}
}
private bool VerifyCredentials ( )
{
// ...
}
}好的:
class UserAuth
{
private User User ;
public UserAuth ( User user )
{
User = user ;
}
public bool VerifyCredentials ( )
{
// ...
}
}
class UserSettings
{
private User User ;
private UserAuth Auth ;
public UserSettings ( User user )
{
User = user ;
Auth = new UserAuth ( user ) ;
}
public void ChangeSettings ( Settings settings )
{
if ( Auth . VerifyCredentials ( ) )
{
// ...
}
}
}⬆回到顶部
如Bertrand Meyer所说,“软件实体(类,模块,功能等)应开放以进行扩展,但要进行修改。”那是什么意思?该原则基本上指出,您应该允许用户在不更改现有代码的情况下添加新功能。
坏的:
abstract class AdapterBase
{
protected string Name ;
public string GetName ( )
{
return Name ;
}
}
class AjaxAdapter : AdapterBase
{
public AjaxAdapter ( )
{
Name = "ajaxAdapter" ;
}
}
class NodeAdapter : AdapterBase
{
public NodeAdapter ( )
{
Name = "nodeAdapter" ;
}
}
class HttpRequester : AdapterBase
{
private readonly AdapterBase Adapter ;
public HttpRequester ( AdapterBase adapter )
{
Adapter = adapter ;
}
public bool Fetch ( string url )
{
var adapterName = Adapter . GetName ( ) ;
if ( adapterName == "ajaxAdapter" )
{
return MakeAjaxCall ( url ) ;
}
else if ( adapterName == "httpNodeAdapter" )
{
return MakeHttpCall ( url ) ;
}
}
private bool MakeAjaxCall ( string url )
{
// request and return promise
}
private bool MakeHttpCall ( string url )
{
// request and return promise
}
}好的:
interface IAdapter
{
bool Request ( string url ) ;
}
class AjaxAdapter : IAdapter
{
public bool Request ( string url )
{
// request and return promise
}
}
class NodeAdapter : IAdapter
{
public bool Request ( string url )
{
// request and return promise
}
}
class HttpRequester
{
private readonly IAdapter Adapter ;
public HttpRequester ( IAdapter adapter )
{
Adapter = adapter ;
}
public bool Fetch ( string url )
{
return Adapter . Request ( url ) ;
}
}⬆回到顶部
对于一个非常简单的概念来说,这是一个可怕的术语。它正式定义为“如果s是t的子类型,则可以用类型s的对象替换T型的对象(即S类型S类型的对象可以代替T型的对象)而不会更改该程序的任何理想属性(正确性,任务,执行等)。”这是一个更可怕的定义。
最好的解释是,如果您有父母课程和子类,那么基类和子类可以互换使用而不会获得不正确的结果。这可能仍然令人困惑,所以让我们来看看经典的Square Rectangle示例。从数学上讲,一个正方形是矩形,但是如果您使用继承使用“ IS-A”关系对其进行建模,那么您很快就会陷入困境。
坏的:
class Rectangle
{
protected double Width = 0 ;
protected double Height = 0 ;
public Drawable Render ( double area )
{
// ...
}
public void SetWidth ( double width )
{
Width = width ;
}
public void SetHeight ( double height )
{
Height = height ;
}
public double GetArea ( )
{
return Width * Height ;
}
}
class Square : Rectangle
{
public double SetWidth ( double width )
{
Width = Height = width ;
}
public double SetHeight ( double height )
{
Width = Height = height ;
}
}
Drawable RenderLargeRectangles ( Rectangle rectangles )
{
foreach ( rectangle in rectangles )
{
rectangle . SetWidth ( 4 ) ;
rectangle . SetHeight ( 5 ) ;
var area = rectangle . GetArea ( ) ; // BAD: Will return 25 for Square. Should be 20.
rectangle . Render ( area ) ;
}
}
var rectangles = new [ ] { new Rectangle ( ) , new Rectangle ( ) , new Square ( ) } ;
RenderLargeRectangles ( rectangles ) ;好的:
abstract class ShapeBase
{
protected double Width = 0 ;
protected double Height = 0 ;
abstract public double GetArea ( ) ;
public Drawable Render ( double area )
{
// ...
}
}
class Rectangle : ShapeBase
{
public void SetWidth ( double width )
{
Width = width ;
}
public void SetHeight ( double height )
{
Height = height ;
}
public double GetArea ( )
{
return Width * Height ;
}
}
class Square : ShapeBase
{
private double Length = 0 ;
public double SetLength ( double length )
{
Length = length ;
}
public double GetArea ( )
{
return Math . Pow ( Length , 2 ) ;
}
}
Drawable RenderLargeRectangles ( Rectangle rectangles )
{
foreach ( rectangle in rectangles )
{
if ( rectangle is Square )
{
rectangle . SetLength ( 5 ) ;
}
else if ( rectangle is Rectangle )
{
rectangle . SetWidth ( 4 ) ;
rectangle . SetHeight ( 5 ) ;
}
var area = rectangle . GetArea ( ) ;
rectangle . Render ( area ) ;
}
}
var shapes = new [ ] { new Rectangle ( ) , new Rectangle ( ) , new Square ( ) } ;
RenderLargeRectangles ( shapes ) ;⬆回到顶部
ISP指出:“不应强迫客户依靠他们不使用的接口。”
一个很好的示例可以证明这一原则是针对需要大设置对象的类。不需要客户设置大量选项是有益的,因为大多数时候他们不需要所有设置。使它们可选有助于防止具有“胖界面”。
坏的:
public interface IEmployee
{
void Work ( ) ;
void Eat ( ) ;
}
public class Human : IEmployee
{
public void Work ( )
{
// ....working
}
public void Eat ( )
{
// ...... eating in lunch break
}
}
public class Robot : IEmployee
{
public void Work ( )
{
//.... working much more
}
public void Eat ( )
{
//.... robot can't eat, but it must implement this method
}
}好的:
并非每个工人都是雇员,但是每个雇员都是工人。
public interface IWorkable
{
void Work ( ) ;
}
public interface IFeedable
{
void Eat ( ) ;
}
public interface IEmployee : IFeedable , IWorkable
{
}
public class Human : IEmployee
{
public void Work ( )
{
// ....working
}
public void Eat ( )
{
//.... eating in lunch break
}
}
// robot can only work
public class Robot : IWorkable
{
public void Work ( )
{
// ....working
}
}⬆回到顶部
该原则指出了两件基本的事情:
一开始可能很难理解,但是如果您使用.NET/.NET核心框架,则您已经以依赖注入(DI)的形式看到了该原则的实现。虽然它们不是相同的概念,但DIP可以防止高级模块知道其低级模块的细节并设置它们。它可以通过DI实现这一目标。这样一个巨大的好处是,它减少了模块之间的耦合。耦合是一种非常糟糕的开发模式,因为它使您的代码难以重构。
坏的:
public abstract class EmployeeBase
{
protected virtual void Work ( )
{
// ....working
}
}
public class Human : EmployeeBase
{
public override void Work ( )
{
//.... working much more
}
}
public class Robot : EmployeeBase
{
public override void Work ( )
{
//.... working much, much more
}
}
public class Manager
{
private readonly Robot _robot ;
private readonly Human _human ;
public Manager ( Robot robot , Human human )
{
_robot = robot ;
_human = human ;
}
public void Manage ( )
{
_robot . Work ( ) ;
_human . Work ( ) ;
}
}好的:
public interface IEmployee
{
void Work ( ) ;
}
public class Human : IEmployee
{
public void Work ( )
{
// ....working
}
}
public class Robot : IEmployee
{
public void Work ( )
{
//.... working much more
}
}
public class Manager
{
private readonly IEnumerable < IEmployee > _employees ;
public Manager ( IEnumerable < IEmployee > employees )
{
_employees = employees ;
}
public void Manage ( )
{
foreach ( var employee in _employees )
{
_employee . Work ( ) ;
}
}
}⬆回到顶部
尝试观察干燥原则。
尽力避免重复代码。重复的代码很糟糕,因为这意味着如果您需要更改某些逻辑,则有多个地方可以更改某些内容。
想象一下,如果您经营一家餐厅,并且可以跟踪库存:所有西红柿,洋葱,大蒜,香料等。如果您有多个列表,则必须在其中搭配西红柿一起食用一道菜时都必须进行更新。如果您只有一个列表,则只有一个可以更新的地方!
通常,您有重复的代码,因为您有两个或更多稍微不同的东西,它们有很多共同点,但是它们的差异迫使您拥有两个或多个独立的功能,可以做很多相同的事情。删除重复代码意味着创建一个可以使用一个功能/模块/类来处理这组不同事物的抽象。
正确获取抽象至关重要,这就是为什么您应该遵循课程中规定的可靠原则的原因。不良的抽象可能比重复的代码差,所以要小心!话虽如此,如果您可以做出一个良好的抽象,那就去做吧!不要重复自己,否则您会发现自己想更改一件事时会更新多个位置。
坏的:
public List < EmployeeData > ShowDeveloperList ( Developers developers )
{
foreach ( var developers in developer )
{
var expectedSalary = developer . CalculateExpectedSalary ( ) ;
var experience = developer . GetExperience ( ) ;
var githubLink = developer . GetGithubLink ( ) ;
var data = new [ ] {
expectedSalary ,
experience ,
githubLink
} ;
Render ( data ) ;
}
}
public List < ManagerData > ShowManagerList ( Manager managers )
{
foreach ( var manager in managers )
{
var expectedSalary = manager . CalculateExpectedSalary ( ) ;
var experience = manager . GetExperience ( ) ;
var githubLink = manager . GetGithubLink ( ) ;
var data =
new [ ] {
expectedSalary ,
experience ,
githubLink
} ;
render ( data ) ;
}
}好的:
public List < EmployeeData > ShowList ( Employee employees )
{
foreach ( var employee in employees )
{
var expectedSalary = employees . CalculateExpectedSalary ( ) ;
var experience = employees . GetExperience ( ) ;
var githubLink = employees . GetGithubLink ( ) ;
var data =
new [ ] {
expectedSalary ,
experience ,
githubLink
} ;
render ( data ) ;
}
}非常好:
最好使用CODE的紧凑版本。
public List < EmployeeData > ShowList ( Employee employees )
{
foreach ( var employee in employees )
{
render ( new [ ] {
employee . CalculateExpectedSalary ( ) ,
employee . GetExperience ( ) ,
employee . GetGithubLink ( )
} ) ;
}
}⬆回到顶部
测试比运输更重要。如果您没有测试或数量不足,那么每次发货代码时,您都不会确定自己没有破坏任何东西。确定构成足够数量的什么取决于您的团队,但是拥有100%的覆盖范围(所有声明和分支机构)是您如何获得很高的信心和开发人员的安心。这意味着,除了拥有出色的测试框架外,您还需要使用良好的覆盖范围工具。
没有借口不写测试。有很多优质的.NET测试框架,因此请找到您的团队更喜欢的测试框架。当您找到一个适合团队工作的人时,请始终为您介绍的每个新功能/模块编写测试。如果您的首选方法是测试驱动的开发(TDD),那很棒,但是要点是要确保在启动任何功能或重构现有功能之前要达到覆盖目标。
确保您的测试专注于激光,并且不要测试杂交(无关)的事物,这会迫使AAA PATERN用来使您的代码更加干净和可读。
坏的:
public class MakeDotNetGreatAgainTests
{
[ Fact ]
public void HandleDateBoundaries ( )
{
var date = new MyDateTime ( "1/1/2015" ) ;
date . AddDays ( 30 ) ;
Assert . Equal ( "1/31/2015" , date ) ;
date = new MyDateTime ( "2/1/2016" ) ;
date . AddDays ( 28 ) ;
Assert . Equal ( "02/29/2016" , date ) ;
date = new MyDateTime ( "2/1/2015" ) ;
date . AddDays ( 28 ) ;
Assert . Equal ( "03/01/2015" , date ) ;
}
}好的:
public class MakeDotNetGreatAgainTests
{
[ Fact ]
public void Handle30DayMonths ( )
{
// Arrange
var date = new MyDateTime ( "1/1/2015" ) ;
// Act
date . AddDays ( 30 ) ;
// Assert
Assert . Equal ( "1/31/2015" , date ) ;
}
[ Fact ]
public void HandleLeapYear ( )
{
// Arrange
var date = new MyDateTime ( "2/1/2016" ) ;
// Act
date . AddDays ( 28 ) ;
// Assert
Assert . Equal ( "02/29/2016" , date ) ;
}
[ Fact ]
public void HandleNonLeapYear ( )
{
// Arrange
var date = new MyDateTime ( "2/1/2015" ) ;
// Act
date . AddDays ( 28 ) ;
// Assert
Assert . Equal ( "03/01/2015" , date ) ;
}
}soure https://www.codingblocks.net/podcast/how-to-to-write-amazing-unit-tests
⬆回到顶部
异步编程指南的摘要
| 姓名 | 描述 | 例外 |
|---|---|---|
| 避免异步无效 | 优于异步任务方法而不是异步空隙方法 | 活动处理程序 |
| 一路异步 | 不要混合阻塞和异步代码 | 控制台主方法(C#<= 7.0) |
| 配置上下文 | 可以在可以的情况下使用ConfigureAwait(false) | 需要上下文的方法 |
做事的异步方式
| 为此... | 而不是这个... | 使用此 |
|---|---|---|
| 检索背景任务的结果 | Task.Wait or Task.Result | await |
| 等待任何任务完成 | Task.WaitAny | await Task.WhenAny |
| 检索多个任务的结果 | Task.WaitAll | await Task.WhenAll |
| 等待一段时间 | Thread.Sleep | await Task.Delay |
最佳实践
异步/等待是IO绑定任务的最佳选择(网络通信,数据库通信,HTTP请求等),但应用于计算界限任务(在巨大列表上,呈现ungge image等)不好。因为它将将固定线程释放到线程池,而可用的CPU/内核将不涉及处理这些任务。因此,我们应该避免使用异步/等待构成任务。
对于处理计算绑定的任务,更喜欢使用Task.Factory.CreateNew TaskCreationOptions是LongRunning 。它将启动一个新的背景线程,以处理一个重型计算绑定任务,而无需将其释放回线池,直到完成任务为止。
知道您的工具
有很多关于异步和等待的需要学习的东西,很自然会有一些迷失方向。这是对常见问题的解决方案的快速参考。
常见异步问题的解决方案
| 问题 | 解决方案 |
|---|---|
| 创建一个任务以执行代码 | Task.Run或TaskFactory.StartNew (不是Task构建器或Task.Start ) |
| 为操作或事件创建任务包装器 | TaskFactory.FromAsync或TaskCompletionSource<T> |
| 支持取消 | CancellationTokenSource和CancellationToken |
| 报告进度 | IProgress<T>和Progress<T> |
| 处理数据流 | TPL数据流或反应性扩展 |
| 同步访问共享资源 | SemaphoreSlim |
| 异步初始化资源 | AsyncLazy<T> |
| 异步准备的生产商/消费者结构 | tpl dataflow或AsyncCollection<T> |
阅读基于任务的异步模式(TAP)文档。它写得非常好,包括有关API设计的指南和适当使用异步/等待(包括取消和进度报告)。
应该使用许多新的等待友好的技术,而不是旧的阻止技术。如果您在新的async代码中有这些旧示例中的任何一个,那么您做错了(TM):
| 老的 | 新的 | 描述 |
|---|---|---|
task.Wait | await task | 等待/等待任务完成 |
task.Result | await task | 获得完成任务的结果 |
Task.WaitAny | await Task.WhenAny | 等待/等待完成的一项任务之一 |
Task.WaitAll | await Task.WhenAll | 等待/等待每项任务中的每一个完成 |
Thread.Sleep | await Task.Delay | 等待/等待一段时间 |
Task构造函数 | Task.Run或TaskFactory.StartNew | 创建基于代码的任务 |
来源https://gist.github.com/jonlabelle/841146854B23B305B50FA5542F84B20C
⬆回到顶部
扔错误是一件好事!它们意味着,当您的程序中的某些内容出现问题时,运行时间已成功识别,并且通过在当前堆栈上停止函数执行,杀死该过程(以.NET/.NET CORE)并通过堆栈跟踪在控制台中通知您,从而让您知道。
如果您需要在捕获它后重新验证异常,请使用此功能“仅使用”,可以保存堆栈跟踪。但是在下面的不良选择中,您将丢失堆栈跟踪。
坏的:
try
{
// Do something..
}
catch ( Exception ex )
{
// Any action something like roll-back or logging etc.
throw ex ;
}好的:
try
{
// Do something..
}
catch ( Exception ex )
{
// Any action something like roll-back or logging etc.
throw ;
}⬆回到顶部
无需抓取错误而无所事事并不能使您能够解决或对所述错误做出反应。扔错误并不多,因为它在印刷到控制台的东西的海洋中会丢失。如果您在try/catch中包装任何代码,则意味着您认为可能发生错误,因此您应该有一个计划或创建代码路径,以便在发生时。
坏的:
try
{
FunctionThatMightThrow ( ) ;
}
catch ( Exception ex )
{
// silent exception
}好的:
try
{
FunctionThatMightThrow ( ) ;
}
catch ( Exception error )
{
NotifyUserOfError ( error ) ;
// Another option
ReportErrorToService ( error ) ;
}⬆回到顶部
如果您需要根据例外类型采取行动,则最好将多个捕获块用于异常处理。
坏的:
try
{
// Do something..
}
catch ( Exception ex )
{
if ( ex is TaskCanceledException )
{
// Take action for TaskCanceledException
}
else if ( ex is TaskSchedulerException )
{
// Take action for TaskSchedulerException
}
}好的:
try
{
// Do something..
}
catch ( TaskCanceledException ex )
{
// Take action for TaskCanceledException
}
catch ( TaskSchedulerException ex )
{
// Take action for TaskSchedulerException
}⬆回到顶部
C#允许使用throw关键字将异常在捕获块中复位。使用throw e; 。此语句重置堆栈跟踪。而是使用throw; 。这将保持堆栈跟踪并提供有关例外的更深入的见解。另一个选择是使用自定义。只需实例化一个新的异常,然后将其内部异常属性设置为带有new CustomException("some info", e); 。将信息添加到例外是一个很好的做法,因为它将有助于调试。但是,如果目标是记录例外,请使用throw;向呼叫者传递责任。
坏的:
try
{
FunctionThatMightThrow ( ) ;
}
catch ( Exception ex )
{
logger . LogInfo ( ex ) ;
throw ex ;
}好的:
try
{
FunctionThatMightThrow ( ) ;
}
catch ( Exception error )
{
logger . LogInfo ( error ) ;
throw ;
}好的:
try
{
FunctionThatMightThrow ( ) ;
}
catch ( Exception error )
{
logger . LogInfo ( error ) ;
throw new CustomException ( error ) ;
}⬆回到顶部
坏的:
项目中具有许多代码格式样式。例如,缩进样式是项目中的space和tab 。
好的:
使用.editorconfig文件在代码库中定义并保持一致的代码样式
root = true
[ * ]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf - 8
trim_trailing_whitespace = true
insert_final_newline = true
# C# files
[ * . cs ]
indent_size = 4
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_within_query_expression_clauses = true
# Code files
[ * . { cs , csx , vb , vbx } ]
indent_size = 4
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
# avoid this . unless absolutely necessary
dotnet_style_qualification_for_field = false : suggestion
dotnet_style_qualification_for_property = false : suggestion
dotnet_style_qualification_for_method = false : suggestion
dotnet_style_qualification_for_event = false : suggestion
# only use var when it's obvious what the variable type is
# csharp_style_var_for_built_in_types = false : none
# csharp_style_var_when_type_is_apparent = false : none
# csharp_style_var_elsewhere = false : suggestion
# use language keywords instead of BCL types
dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion
dotnet_style_predefined_type_for_member_access = true : suggestion
# name all constant fields using PascalCase
dotnet_naming_rule . constant_fields_should_be_pascal_case . severity = suggestion
dotnet_naming_rule . constant_fields_should_be_pascal_case . symbols = constant_fields
dotnet_naming_rule . constant_fields_should_be_pascal_case . style = pascal_case_style
dotnet_naming_symbols . constant_fields . applicable_kinds = field
dotnet_naming_symbols . constant_fields . required_modifiers = const
dotnet_naming_style . pascal_case_style . capitalization = pascal_case
# static fields should have s_ prefix
dotnet_naming_rule . static_fields_should_have_prefix . severity = suggestion
dotnet_naming_rule . static_fields_should_have_prefix . symbols = static_fields
dotnet_naming_rule . static_fields_should_have_prefix . style = static_prefix_style
dotnet_naming_symbols . static_fields . applicable_kinds = field
dotnet_naming_symbols . static_fields . required_modifiers = static
dotnet_naming_style . static_prefix_style . required_prefix = s_
dotnet_naming_style . static_prefix_style . capitalization = camel_case
# i nternal and private fields should be _camelCase
dotnet_naming_rule . camel_case_for_private_internal_fields . severity = suggestion
dotnet_naming_rule . camel_case_for_private_internal_fields . symbols = private_internal_fields
dotnet_naming_rule . camel_case_for_private_internal_fields . style = camel_case_underscore_style
dotnet_naming_symbols . private_internal_fields . applicable_kinds = field
dotnet_naming_symbols . private_internal_fields . applicable_accessibilities = private , internal
dotnet_naming_style . camel_case_underscore_style . required_prefix = _
dotnet_naming_style . camel_case_underscore_style . capitalization = camel_case
# Code style defaults
dotnet_sort_system_directives_first = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
# Expression - level preferences
dotnet_style_object_initializer = true : suggestion
dotnet_style_collection_initializer = true : suggestion
dotnet_style_explicit_tuple_names = true : suggestion
dotnet_style_coalesce_expression = true : suggestion
dotnet_style_null_propagation = true : suggestion
# Expression - bodied members
csharp_style_expression_bodied_methods = false : none
csharp_style_expression_bodied_constructors = false : none
csharp_style_expression_bodied_operators = false : none
csharp_style_expression_bodied_properties = true : none
csharp_style_expression_bodied_indexers = true : none
csharp_style_expression_bodied_accessors = true : none
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true : suggestion
csharp_style_pattern_matching_over_as_with_null_check = true : suggestion
csharp_style_inlined_variable_declaration = true : suggestion
# Null checking preferences
csharp_style_throw_expression = true : suggestion
csharp_style_conditional_delegate_call = true : suggestion
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
[ * . { asm , inc } ]
indent_size = 8
# Xml project files
[ * . { csproj , vcxproj , vcxproj . filters , proj , nativeproj , locproj } ]
indent_size = 2
# Xml config files
[ * . { props , targets , config , nuspec } ]
indent_size = 2
[ CMakeLists . txt ]
indent_size = 2
[ * . cmd ]
indent_size = 2⬆回到顶部
他们通常只是添加噪音。让函数和可变名称以及正确的凹痕和格式化为您的代码提供视觉结构。
坏的:
////////////////////////////////////////////////////////////////////////////////
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
var model = new [ ]
{
menu : 'foo' ,
nav : 'bar'
} ;
////////////////////////////////////////////////////////////////////////////////
// Action setup
////////////////////////////////////////////////////////////////////////////////
void Actions ( )
{
// ...
} ;坏的:
#region Scope Model Instantiation
var model = {
menu : 'foo' ,
nav : 'bar'
} ;
#endregion
#region Action setup
void Actions ( ) {
// ...
} ;
#endregion好的:
var model = new [ ]
{
menu : 'foo' ,
nav : 'bar'
} ;
void Actions ( )
{
// ...
} ;⬆回到顶部
存在版本控制是有原因的。在您的历史中留下旧代码。
坏的:
doStuff ( ) ;
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();好的:
doStuff ( ) ;⬆回到顶部
请记住,使用版本控制!无需死亡代码,评论代码,尤其是日记评论。使用git log获取历史记录!
坏的:
/**
* 2018-12-20: Removed monads, didn't understand them (RM)
* 2017-10-01: Improved using special monads (JP)
* 2016-02-03: Removed type-checking (LI)
* 2015-03-14: Added combine with type-checking (JR)
*/
public int Combine ( int a , int b )
{
return a + b ;
}好的:
public int Combine ( int a , int b )
{
return a + b ;
}⬆回到顶部
评论是道歉,而不是要求。好代码主要是文档本身。
坏的:
public int HashIt ( string data )
{
// The hash
var hash = 0 ;
// Length of string
var length = data . length ;
// Loop through every character in data
for ( var i = 0 ; i < length ; i ++ )
{
// Get character code.
const char = data . charCodeAt ( i ) ;
// Make the hash
hash = ( ( hash << 5 ) - hash ) + char ;
// Convert to 32-bit integer
hash &= hash ;
}
}更好但仍然很糟糕:
public int HashIt ( string data )
{
var hash = 0 ;
var length = data . length ;
for ( var i = 0 ; i < length ; i ++ )
{
const char = data . charCodeAt ( i ) ;
hash = ( ( hash << 5 ) - hash ) + char ;
// Convert to 32-bit integer
hash &= hash ;
}
}如果评论解释了代码在做什么,则可能是无用的评论,可以用一个命名的变量或函数来实现。上一个代码中的评论可以用名为ConvertTo32bitInt的函数替换,因此此评论仍然没有用。但是,很难通过代码来表达为什么开发人员选择DJB2哈希算法而不是SHA-1或其他哈希函数。在这种情况下,评论是可以接受的。
好的:
public int Hash ( string data )
{
var hash = 0 ;
var length = data . length ;
for ( var i = 0 ; i < length ; i ++ )
{
var character = data [ i ] ;
// use of djb2 hash algorithm as it has a good compromise
// between speed and low collision with a very simple implementation
hash = ( ( hash << 5 ) - hash ) + character ;
hash = ConvertTo32BitInt ( hash ) ;
}
return hash ;
}
private int ConvertTo32BitInt ( int value )
{
return value & value ;
}⬆回到顶部
感谢所有已经为clean-code-dotnet项目做出贡献的人
热爱我们的工作并帮助我们继续我们的活动? [成为支持者]
成为赞助商,并在Github上的读书中获取您的徽标,并带有指向您网站的链接。 [成为赞助商]
根据法律的可能,Thangchung放弃了这项工作的所有版权以及相关或邻近的权利。