网站首页 文章专栏 适用于 .NET/.NET Core 的整洁代码实践指南
写给想把代码从“能跑”变成“好读、好测、好维护”的开发者。本文基于你提供的 .NET/.NET Core 整洁代码要点,保留原始代码片段并按主题整理,便于直接贴到博客发布与团队共享。
整洁代码不是风格表面功夫,而是一套让代码长期可读、可重构、可测试的思维方式。下面按 命名、变量、函数、代码组织与依赖 四大维度,保留示例代码并给出要点与实践建议,帮助你在日常开发中逐步落地。
要点:命名要表达意图、可搜索、可重构,避免模糊或误导性命名。
Bad
int d;
Good
int daySinceModification;
Bad
var dataFromDb = db.GetFromService().ToList();
Good
var listOfEmployee = _employeeService.GetEmployees().ToList();
Bad
int iCounter;
string strFullName;
DateTime dModifiedDate;
Good
int counter;
string fullName;
DateTime modifiedDate;
参数也不要带类型前缀:
Bad
public bool IsShopOpen(string pDay, int pAmount)
{
// some logic
}
Good
public bool IsShopOpen(string day, int amount)
{
// some logic
}
Bad
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 {}
Good
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 {}
Bad
public class Employee
{
public Datetime sWorkDate { get; set; } // what the heck is this
public Datetime modTime { get; set; } // same here
}
Good
public class Employee
{
public Datetime StartWorkingDate { get; set; }
public Datetime ModificationTime { get; set; }
}
Good
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();
}
要点:变量命名要可读、可搜索,避免魔法字符串、重复上下文与深层嵌套。
Bad
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;
}
}
Good
public bool IsShopOpen(string day)
{
if (string.IsNullOrEmpty(day))
{
return false;
}
var openingDays = new[] { "friday", "saturday", "sunday" };
return openingDays.Any(d => d == day.ToLower());
}
另一个示例(Fibonacci):
Bad
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");
}
}
Good
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);
}
Bad
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);
}
Good
var locations = new[] { "Austin", "New York", "San Francisco" };
foreach (var location in locations)
{
DoStuff();
DoSomeOtherStuff();
// ...
// ...
// ...
Dispatch(location);
}
Bad
if (userRole == "Admin")
{
// logic in here
}
Good
const string ADMIN_ROLE = "Admin"
if (userRole == ADMIN_ROLE)
{
// logic in here
}
Bad
public class Car
{
public string CarMake { get; set; }
public string CarModel { get; set; }
public string CarColor { get; set; }
//...
}
Good
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public string Color { get; set; }
//...
}
Bad
// 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());
Good
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());
另一个示例(枚举替代魔法数字):
Bad
var data = new { Name = "John", Age = 42, PersonAccess = 4};
// What the heck is 4 for?
if (data.PersonAccess == 4)
{
// do edit ...
}
Good
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 ...
}
Not good
public void CreateMicrobrewery(string name = null)
{
var breweryName = !string.IsNullOrEmpty(name) ? name : "Hipster Brew Co.";
// ...
}
Good
public void CreateMicrobrewery(string breweryName = "Hipster Brew Co.")
{
// ...
}
要点:函数应短小、单一职责、无不必要副作用,命名要清晰,参数尽量少。
Bad
// 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 string SplitIntoFirstAndLastName()
{
return name.Split(" ");
}
SplitIntoFirstAndLastName();
Console.PrintLine(name); // ['Ryan', 'McDermott'];
Good
public string SplitIntoFirstAndLastName(string name)
{
return name.Split(" ");
}
var name = 'Ryan McDermott';
var newName = SplitIntoFirstAndLastName(name);
Console.PrintLine(name); // 'Ryan McDermott';
Console.PrintLine(newName); // ['Ryan', 'McDermott'];
Bad
public bool IsDOMNodeNotPresent(string node)
{
// ...
}
if (!IsDOMNodeNotPresent(node))
{
// ...
}
Good
public bool IsDOMNodePresent(string node)
{
// ...
}
if (IsDOMNodePresent(node))
{
// ...
}
Bad
class Airplane
{
// ...
public double GetCruisingAltitude()
{
switch (_type)
{
case '777':
return GetMaxAltitude() - GetPassengerCount();
case 'Air Force One':
return GetMaxAltitude();
case 'Cessna':
return GetMaxAltitude() - GetFuelExpenditure();
}
}
}
Good
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();
}
}
Bad
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"));
}
}
Good
public Path TravelToTexas(Traveler vehicle)
{
vehicle.TravelTo(new Location("texas"));
}
或使用模式匹配:
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"));
}
}
Bad
public void CreateFile(string name, bool temp = false)
{
if (temp)
{
Touch("./temp/" + name);
}
else
{
Touch(name);
}
}
Good
public void CreateFile(string name)
{
Touch(name);
}
public void CreateTempFile(string name)
{
Touch("./temp/" + name);
}
Bad
public string[] Config()
{
return [
"foo" => "bar",
]
}
Good
class Configuration
{
private string[] _configuration = [];
public Configuration(string[] configuration)
{
_configuration = configuration;
}
public string[] Get(string key)
{
return (_configuration[key]!= null) ? _configuration[key] : null;
}
}
单例反模式示例:
Bad
class DBConnection
{
private static DBConnection _instance;
private DBConnection()
{
// ...
}
public static GetInstance()
{
if (_instance == null)
{
_instance = new DBConnection();
}
return _instance;
}
// ...
}
var singleton = DBConnection.GetInstance();
Good
class DBConnection
{
public DBConnection(IOptions<DbConnectionOption> options)
{
// ...
}
// ...
}
通过 Options 模式与 DI 创建实例:
var options = <resolve from IOC>;
var connection = new DBConnection(options);
Bad
public void CreateMenu(string title, string body, string buttonText, bool cancellable)
{
// ...
}
Good
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)
{
// ...
}
Bad
public void SendEmailToListOfClients(string[] clients)
{
foreach (var client in clients)
{
var clientRecord = db.Find(client);
if (clientRecord.IsActive())
{
Email(client);
}
}
}
Good
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");
}
Bad
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();
Good
public class Email
{
//...
public void Send()
{
SendMail(this._to, this._subject, this._body);
}
}
var message = new Email(...);
// Clear and obvious
message.Send();
示例展示了如何把复杂解析逻辑拆分为 Tokenizer、Lexer 与 Parser,并通过依赖注入组合:
Bad(混合多层抽象)
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...
}
}
Good(拆分为类)
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...
}
}
}
Bad
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();
Good
class PerformanceReview
{
private readonly Employee _employee;
public PerformanceReview(Employee employee)
{
_employee = employee;
}
public PerfReviewData PerfReview()
{
GetPeerReviews();
GetManagerReview();
GetSelfReview();
}
要点:通过接口与依赖注入降低耦合,避免全局状态与单例反模式,使用 Options 模式管理配置。
