clean code dotnet

C#源码 2025-08-14

适用于.NET/.NET核心的清洁代码概念

如果您喜欢clean-code-dotnet项目,或者如果对您有所帮助,请为此存储库提供一颗星。这不仅将有助于加强我们的.NET社区,而且还可以提高世界各地.NET开发人员的清洁代码技能。非常感谢?

查看我的博客或在Twitter上打招呼!

目录

  • 适用于.NET/.NET核心的清洁代码概念
  • 目录
  • 介绍
  • 清洁代码.NET
    • 命名
    • 变量
    • 功能
    • 对象和数据结构
    • 课程
    • 坚硬的
    • 测试
    • 并发
    • 错误处理
    • 格式化
    • 评论
  • 其他干净的代码资源
    • 其他干净的代码列表
    • 样式指南
    • 工具
    • 作弊表
  • 贡献者
  • 支持者
  • 赞助商
  • 执照

介绍

罗伯特·马丁 Robert C.这不是样式指南。它是在.NET/.NET Core中生产可读,可重复使用和可重构软件的指南。

并非必须严格遵循此处的所有原则,甚至更少会被普遍同意。这些是准则,仅此而已,但是它们是在多年的集体经验中由清洁代码的集体经验编纂的。

灵感来自清洁代码javaScript和Clean-ode-PHP列表。

清洁代码.NET

命名

避免使用坏名称好名字允许许多开发人员使用代码。名称应反映其所做的事情并给出上下文。

坏的:

 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 ( ) ;

⬆回到顶部

使用可搜索的名称(第1部分)

我们将读取的代码超出我们将要写的代码。重要的是,我们编写的代码是可以读取且可搜索的。通过命名最终对于理解我们的计划有意义的变量,我们伤害了读者。使您的名字可搜索。

坏的:

 // 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 ( ) ) ;

⬆回到顶部

使用可搜索的名称(第2部分)

坏的:

 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 
							
下载源码

通过命令行克隆项目:

git clone https://github.com/thangchung/clean-code-dotnet.git