clean-code-dotnetプロジェクトが気に入った場合、またはそれが役立った場合は、このリポジトリのスターを贈ってください。それは私たちの.NETコミュニティを強化するのに役立つだけでなく、世界中の.NET開発者のクリーンコードに関するスキルを向上させます。どうもありがとうございます ?
私のブログをチェックするか、Twitterでこんにちは!
Robert C. Martinの本Clean Codeからのソフトウェアエンジニアリングの原則は、.NET/.NET Coreに適合しています。これはスタイルガイドではありません。これは、.NET/.NET Coreで読み取り可能な、再利用可能な、リファクタリング可能なソフトウェアを作成するためのガイドです。
ここのすべての原則が厳密に従う必要があるわけではなく、さらに少ないものが普遍的に合意されるわけではありません。これらはガイドラインであり、それ以上のものではありませんが、 Clean Codeの著者によって長年にわたって集団的経験を体験したものです。
Clean-Code-JavaScriptおよびClean-Code-PHPリストからインスピレーションを受けました。
悪い:
int d ;良い:
int daySinceModification ;topに戻ります
変数に名前を付けて、使用しているものを反映してください。
悪い:
var dataFromDb = db . GetFromService ( ) . ToList ( ) ;良い:
var listOfEmployee = _employeeService . GetEmployees ( ) . ToList ( ) ;topに戻ります
ハンガリーの表記法は、宣言に既に存在するタイプを修正します。現代の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
}topに戻ります
大文字は、変数、関数などについて多くのことを教えてくれます。これらのルールは主観的であるため、チームが必要なものを選択できます。ポイントは、皆さんが何を選んでも、一貫性を保つことです。
悪い:
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 { }topに戻ります
変数が顕著でない場合に、変数と関数の意味を調査するのに時間がかかります。
悪い:
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 ; }
}topに戻ります
変数およびメソッドパラメーターにはCamelCase表記を使用します。
悪い:
var employeephone ;
public double CalculateSalary ( int workingdays , int workinghours )
{
// some logic
}良い:
var employeePhone ;
public double CalculateSalary ( int workingDays , int workingHours )
{
// some logic
}topに戻ります
あなたのコードを読む人もプログラマーです。物事を正しく命名すると、誰もが同じページにいるのに役立ちます。私たちは、変数や関数が何のためのものであるかを皆に説明するために時間をかけたくありません。
良い
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 ( ) ;
}topに戻ります
他のステートメントがコードの従うのを難しくすることができる場合、多すぎる。明示的なのは暗黙的なものよりも優れています。
悪い:
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 ) ;
}topに戻ります
あなたのコードの読者に変数の意味を翻訳するように強制しないでください。明示的なのは暗黙的なものよりも優れています。
悪い:
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 ) ;
}topに戻ります
マジック文字列は、アプリケーションの動作に影響を与えるアプリケーションコード内で直接指定された文字列値です。多くの場合、このような文字列はシステム内で複製され、リファクタリングツールを使用して自動的に更新できないため、一部の文字列ではなく他の文字列に変更が行われると、バグの一般的なソースになります。
悪い
if ( userRole == "Admin" )
{
// logic in here
}良い
const string ADMIN_ROLE = "Admin"
if ( userRole == ADMIN_ROLE )
{
// logic in here
}これを使用すると、一元化する場所を変更するだけで、他の人はそれを適応させます。
topに戻ります
クラス/オブジェクト名が何かを教えてくれる場合は、可変名でそれを繰り返さないでください。
悪い:
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 ; }
//...
}topに戻ります
悪い:
var ymdstr = DateTime . UtcNow . ToString ( "MMMM dd, yyyy" ) ;良い:
var currentDate = DateTime . UtcNow . ToString ( "MMMM dd, yyyy" ) ;topに戻ります
悪い:
GetUserInfo ( ) ;
GetUserData ( ) ;
GetUserRecord ( ) ;
GetUserProfile ( ) ;良い:
GetUser ( ) ;topに戻ります
これまで以上にコードを読みます。私たちが書いているコードが読みやすく検索可能であることが重要です。プログラムを理解するために意味のある変数を命名しないことで、読者を傷つけます。あなたの名前を検索可能にします。
悪い:
// 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 ( ) ) ;topに戻ります
悪い:
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 ...
}topに戻ります
悪い:
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 ) ;
}topに戻ります
良くない:
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." )
{
// ...
}topに戻ります
関数は、値を取得して別の値または値を返す以外に何かを行う場合、副作用を生成します。副作用は、ファイルに書き込み、グローバル変数の変更、またはすべてのお金を見知らぬ人に誤って配線することです。
今、あなたは時々プログラムで副作用を持つ必要があります。前の例のように、ファイルに書き込む必要があるかもしれません。あなたがしたいのは、あなたがこれをしている場所を一元化することです。特定のファイルに書き込む機能やクラスはいくつかありません。それを行う1つのサービスがあります。唯一の1つ。
主なポイントは、構造のないオブジェクト間で状態を共有するなどの一般的な落とし穴を避け、何でも書き込みできる可変データ型を使用し、副作用が発生する場所を一元化することではないことです。これを行うことができれば、他のプログラマーの大多数よりも幸せになります。
悪い:
// 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 McDermotttopに戻ります
悪い:
public bool IsDOMNodeNotPresent ( string node )
{
// ...
}
if ( ! IsDOMNodeNotPresent ( node ) )
{
// ...
}良い:
public bool IsDOMNodePresent ( string node )
{
// ...
}
if ( IsDOMNodePresent ( node ) )
{
// ...
}topに戻ります
これは不可能な作業のようです。これを最初に聞いて、ほとんどの人は「 ifステートメントなしで何かをすることになっているのはどうしてですか?」と言います。答えは、多くの場合、同じタスクを達成するために多型を使用できるということです。 2番目の質問は通常、「まあそれは素晴らしいことですが、なぜ私はそれをしたいのですか?」です。答えは、私たちが学んだ以前のクリーンなコードの概念です。関数は1つのことしかする必要があります。ステートメント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 ( ) ;
}
}topに戻ります
悪い:
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" ) ) ;
}
}topに戻ります
悪い:
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 ;
}topに戻ります
フラグは、この方法に複数の責任があることを示しています。方法に単一の責任しかない場合が最善です。ブールパラメーターがメソッドに複数の責任を追加する場合、メソッドを2つに分割します。
悪い:
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 ) ;
}topに戻ります
グローバルの汚染は、多くの言語で悪い習慣です。なぜなら、別のライブラリと衝突する可能性があり、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のインスタンスを使用する必要があります。
topに戻ります
シングルトンはアンチパターンです。ブライアンボタンから言い換えれば:
また、問題の根本について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のインスタンスを使用する必要があります。
topに戻ります
関数パラメーターの量を制限することは、機能をテストしやすくするため、非常に重要です。 3つ以上のリードを持つと、別々の引数があるさまざまなケースをテストする必要がある組み合わせ爆発につながります。
ゼロ引数が理想的なケースです。 1つまたは2つの議論は大丈夫で、3つを避ける必要があります。それ以上のものは統合されるべきです。通常、2つ以上の引数がある場合、あなたの関数はあまりにも多くのことをしようとしています。そうでない場合、ほとんどの場合、高レベルのオブジェクトでは議論として十分です。
悪い:
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 )
{
// ...
}topに戻ります
これは、ソフトウェアエンジニアリングの最も重要なルールです。関数が複数のことを行う場合、それらは構成、テスト、および推論が困難です。関数を1つのアクションに分離できる場合、それらは簡単にリファクタリングでき、コードはよりクリーンに読み取られます。これ以外のこのガイドから他に何も離れていない場合は、多くの開発者よりも先を行くことになります。
悪い:
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" ) ;
}topに戻ります
悪い:
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 ( ) ;topに戻ります
まだ終わっていません
複数のレベルの抽象化がある場合、機能は通常あまりにも多くのことをしています。機能を分割すると、再利用性とテストが容易になります。
悪い:
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...
}
}
}topに戻ります
関数が別のものを呼び出す場合、それらの関数をソースファイルに垂直に閉じたままにします。理想的には、発信者をCalleeのすぐ上に置いてください。新聞のように、トップからボトムへのコードを読む傾向があります。このため、コードにそのように読んでもらいます。
悪い:
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 ( ) ;topに戻ります
悪い:
if ( article . state == "published" )
{
// ...
}良い:
if ( article . IsPublished ( ) )
{
// ...
}topに戻ります
デッドコードは、重複コードと同じくらい悪いです。コードベースにそれを保持する理由はありません。それが呼ばれていない場合は、それを取り除きましょう!あなたがそれを必要とするなら、それはあなたのバージョンの履歴でまだ安全です。
悪い:
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" ) ;topに戻ります
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 ;topに戻ります
悪い:
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 Doetopに戻ります
このパターンは非常に便利で、多くのライブラリで一般的に使用されています。これにより、コードが表現力豊かで、冗長性が低くなります。そのため、メソッドチェーンを使用して、コードがどれだけきれいになるかを見てください。
良い:
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 ( ) ;
}topに戻ります
4人のギャングによるデザインパターンで有名に述べられているように、あなたはできる限り継承よりも構成を好むべきです。相続財産を使用する理由はたくさんあり、構成を使用する正当な理由がたくさんあります。
この格言の主なポイントは、あなたの心が本能的に相続のために行くなら、構成があなたの問題をより良くモデル化できるかどうかを考えるようにしてください。場合によってはそれができます。
あなたは「いつ相続財産を使うべきか」と疑問に思うかもしれません。それは手元の問題に依存しますが、これは継承が構成よりも理にかなっているときのまともなリストです。
悪い:
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 ) ;
}
// ...
}topに戻ります
SOLIDは、Michael Feathersがロバートマーティンによって名付けられた最初の5つの原則のために導入されたニーモニックの頭字語です。これは、オブジェクト指向のプログラミングとデザインの5つの基本原則を意味します。
Clean Codeで述べたように、「クラスが変更する理由が1つ以上ないはずです」。フライトでスーツケースを1つしか使用できない場合のように、多くの機能を備えたクラスをジャムパックするのは魅力的です。これに関する問題は、あなたのクラスが概念的にまとまりがなく、変化する多くの理由を与えることです。クラスを変更するために必要な回数を最小化することが重要です。
それは重要です。なぜなら、あまりにも多くの機能が1つのクラスにあり、その一部を変更すると、コードベースの他の依存モジュールにどのように影響するかを理解することが困難になる可能性があるためです。
悪い:
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 ( ) )
{
// ...
}
}
}topに戻ります
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 ) ;
}
}topに戻ります
これは非常にシンプルな概念にとって恐ろしい用語です。 「Sがtのサブタイプの場合、タイプtのオブジェクトは、そのプログラムの望ましい特性(正確さ、タスクなど)のいずれかを変更せずにタイプS(つまり、タイプSのオブジェクトをタイプTのオブジェクトに置き換えることができます)のオブジェクトに置き換えることができます。」それはさらに怖い定義です。
これの最良の説明は、親クラスと子供クラスがある場合、ベースクラスと子のクラスを誤った結果を得ることなく同じ意味で使用できることです。これはまだ混乱しているかもしれないので、古典的な四角い角の例を見てみましょう。数学的には、正方形は長方形ですが、継承を介して「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 ) ;topに戻ります
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
}
}topに戻ります
この原則は、2つの重要なことを述べています。
これは最初は理解するのが難しい場合がありますが、.NET/.NETコアフレームワークで作業した場合、依存関係注入(DI)の形でこの原則の実装が見られます。それらは同一の概念ではありませんが、DIPは高レベルのモジュールが低レベルモジュールの詳細を知り、それらをセットアップすることを維持しています。これを介してこれを達成できます。これの大きな利点は、モジュール間の結合を減らすことです。カップリングは、コードがリファクタリングを難しくするため、非常に悪い開発パターンです。
悪い:
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 ( ) ;
}
}
}topに戻ります
乾燥した原理を観察してみてください。
重複コードを避けるために、絶対に最善を尽くします。重複するコードは悪いことです。なぜなら、ロジックを変更する必要がある場合、何かを変更する場所が複数あることを意味するからです。
レストランを経営し、在庫を追跡している場合を想像してください:トマト、玉ねぎ、ニンニク、スパイスなど、これを保持する複数のリストがある場合は、トマトが入った料理を提供するときにすべて更新する必要があります。リストが1つしかない場合、更新する場所は1つだけです!
多くの場合、2つ以上のわずかに異なるものがあり、共通するものを共有しているため、コードが重複していますが、それらの違いにより、同じことを行う2つ以上の個別の機能があります。重複したコードを削除するということは、1つの関数/モジュール/クラスだけでこのさまざまなもののセットを処理できる抽象化を作成することを意味します。
抽象化を正しく取得することが重要です。そのため、クラスセクションに記載されている堅実な原則に従う必要があります。悪い抽象化は重複コードよりも悪い場合があるので、注意してください!これを言って、あなたが良い抽象化をすることができれば、それをしてください!繰り返さないでください。そうしないと、1つを変更したいときはいつでも複数の場所を更新します。
悪い:
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 ) ;
}
}とても良い:
コードのコンパクトバージョンを使用することをお勧めします。
public List < EmployeeData > ShowList ( Employee employees )
{
foreach ( var employee in employees )
{
render ( new [ ] {
employee . CalculateExpectedSalary ( ) ,
employee . GetExperience ( ) ,
employee . GetGithubLink ( )
} ) ;
}
}topに戻ります
テストは出荷よりも重要です。テストや不十分な金額がない場合、コードを出荷するたびに、何も壊れていないことを確認することはできません。適切な金額を構成するものを決定することはあなたのチーム次第ですが、100%のカバレッジ(すべての声明と支店)を持つことは、あなたが非常に高い自信と開発者の安心を達成する方法です。これは、優れたテストフレームワークを持つことに加えて、優れたカバレッジツールを使用する必要があることを意味します。
テストを書かない言い訳はありません。優れた.NETテストフレームワークがたくさんあるので、チームが好むものを見つけてください。チームに合わせて機能するものを見つけたら、紹介するすべての新しい機能/モジュールのテストを常に作成することを目指してください。好みの方法がテスト駆動開発(TDD)である場合、それは素晴らしいことですが、主なポイントは、機能を起動する前にカバレッジの目標を達成するか、既存の目標をリファクタリングすることです。
テストがレーザー焦点であり、その他の(関連性のない)ものをテストしないことを保証します。
悪い:
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-write-amazing-unit-tests
topに戻ります
非同期プログラミングガイドラインの概要
| 名前 | 説明 | 例外 |
|---|---|---|
| Async voidを避けてください | Async voidメソッドよりも非同期タスクメソッドを好みます | イベントハンドラー |
| ずっとasync | ブロッキングと非同期コードを混ぜないでください | メインメソッドをコンソール(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 |
ベストプラクティス
ASYNC/AWAITは、IOバインドされたタスク(ネットワーク通信、データベース通信、HTTPリクエストなど)に最適ですが、計算バインドタスクに適用するのは良くありません(巨大なリストでトラバース、Hugge Imageなど)。保持スレッドがスレッドプールにリリースされるため、利用可能なCPU/コアはそれらのタスクを処理することは含まれません。したがって、コンピューティングバインドされたタスクにAsync/awaitを使用しないでください。
計算バインドさLongRunningたタスクを扱うために、 Task.Factory.CreateNew TaskCreationOptions使用することを好みます。新しいバックグラウンドスレッドを起動して、タスクが完了するまでスレッドプールにリリースすることなく、重い計算バインドタスクを処理します。
あなたのツールを知ってください
Asyncについて学ぶことがたくさんあります。一般的な問題に対する解決策の簡単な参照を次に示します。
一般的な非同期問題の解決策
| 問題 | 解決 |
|---|---|
| コードを実行するタスクを作成します | Task.RunまたはTaskFactory.StartNew ( Task constructorまたはTask.Startではない) |
| 操作またはイベントのタスクラッパーを作成します | TaskFactory.FromAsyncまたはTaskCompletionSource<T> |
| キャンセルをサポートします | CancellationTokenSourceとCancellationToken |
| 進捗を報告します | IProgress<T>およびProgress<T> |
| データのストリームを処理します | TPLデータフローまたはリアクティブ拡張機能 |
| 共有リソースへのアクセスを同期します | SemaphoreSlim |
| リソースを非同期に初期化します | AsyncLazy<T> |
| ASYNC対応の生産者/消費者構造 | TPLデータフローまたはAsyncCollection<T> |
タスクベースの非同期パターン(TAP)ドキュメントをお読みください。これは非常によく書かれており、API設計に関するガイダンスと非同期/待望の適切な使用(キャンセルおよび進捗報告を含む)が含まれています。
古いブロッキングテクニックの代わりに使用する必要がある多くの新しい待ち気のあるテクニックがあります。新しい非同期コードにこれらの古い例がある場合は、間違っていることをしています(TM):
| 古い | 新しい | 説明 |
|---|---|---|
task.Wait | await task | タスクが完了するのを待ってください |
task.Result | await task | 完了したタスクの結果を取得します |
Task.WaitAny | await Task.WhenAny | タスクのコレクションの1つを完了するのを待つ/待つ |
Task.WaitAll | await Task.WhenAll | タスクのコレクションのすべてを完了するのを待つ/待つ |
Thread.Sleep | await Task.Delay | 一定期間待ってください |
Taskコンストラクター | Task.RunまたはTaskFactory.StartNew | コードベースのタスクを作成します |
ソースhttps://gist.github.com/jonlabelle/841146854b23b305b50fa5542f84b20cc
topに戻ります
スローされたエラーは良いことです!彼らは、プログラムの何かが間違っているときにランタイムが正常に識別され、現在のスタックで機能の実行を停止し、プロセスを殺し(.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 ;
}topに戻ります
キャッチされたエラーで何もしないと、前述のエラーを修正または反応する能力が得られません。エラーを投げることは、コンソールに印刷されたものの海で迷子になることが多いため、それほど良くありません。 try/catchでコードのビットをラップする場合、エラーが発生する可能性があるため、それが発生したときに計画を立てるか、コードパスを作成する必要があります。
悪い:
try
{
FunctionThatMightThrow ( ) ;
}
catch ( Exception ex )
{
// silent exception
}良い:
try
{
FunctionThatMightThrow ( ) ;
}
catch ( Exception error )
{
NotifyUserOfError ( error ) ;
// Another option
ReportErrorToService ( error ) ;
}topに戻ります
例外のタイプに従ってアクションを実行する必要がある場合は、例外処理のために複数のキャッチブロックを使用することをお勧めします。
悪い:
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
}topに戻ります
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 ) ;
}topに戻ります
悪い:
プロジェクトには多くのコードフォーマットスタイルがあります。たとえば、インデントスタイルは、プロジェクトでミックスされた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 = 2topに戻ります
彼らは通常、ノイズを追加するだけです。適切なインデントとフォーマットとともに、関数と変数名をコードに視覚構造を提供します。
悪い:
////////////////////////////////////////////////////////////////////////////////
// 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 ( )
{
// ...
} ;topに戻ります
理由のためにバージョン制御が存在します。あなたの歴史に古いコードを残してください。
悪い:
doStuff ( ) ;
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();良い:
doStuff ( ) ;topに戻ります
覚えておいてください、バージョンコントロールを使用してください!死んだコード、コメントコード、特にジャーナルコメントは必要ありません。 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 ;
}topに戻ります
コメントは謝罪であり、要件ではありません。良いコードは主にそれ自体を文書化します。
悪い:
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という名前の関数に置き換えることができるため、このコメントはまだ役に立たない。ただし、開発者がSHA-1または別のハッシュ関数の代わりにDJB2ハッシュアルゴリズムを選択した理由をコードで表現するのは困難です。その場合、コメントは受け入れられます。
良い:
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 ;
}topに戻ります
既にclean-code-dotnetプロジェクトに貢献してくれたすべての人々に感謝します
私たちの仕事を愛し、私たちが私たちの活動を続けるのを助けますか? [支援者になる]
スポンサーになって、サイトへのリンクを使用して、GitHubのReadMeでロゴを入手してください。 [スポンサーになる]
法律の下で可能な範囲で、タントチョンはこの作業に対するすべての著作権および関連するまたは近隣の権利を放棄しました。