mi-assessment/docs/开发规范/1-编程规约/1.2-代码风格.md
2026-02-03 20:50:51 +08:00

824 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 1.2 代码风格
---
## 1. 文件组织结构
- 类成员组织顺序
```csharp
public class OrderService : IOrderService
{
// 1. 常量
private const int MaxRetryCount = 3;
public const string DefaultCurrency = "CNY";
// 2. 静态字段
private static readonly object _lockObject = new object();
// 3. 私有字段(只读字段在前)
private readonly IOrderRepository _orderRepository;
private readonly ILogger<OrderService> _logger;
private readonly IMapper _mapper;
private int _processCount;
// 4. 构造函数
public OrderService(
IOrderRepository orderRepository,
ILogger<OrderService> logger,
IMapper mapper)
{
_orderRepository = orderRepository;
_logger = logger;
_mapper = mapper;
}
// 5. 属性
public int ProcessedCount => _processCount;
// 6. 公共方法(按功能分组)
public async Task<Order> GetOrderAsync(int orderId)
{
// ...existing code...
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
// ...existing code...
}
// 7. 受保护方法
protected virtual bool ValidateOrder(Order order)
{
// ...existing code...
}
// 8. 私有方法
private decimal CalculateDiscount(Order order)
{
// ...existing code...
}
private void LogOrderCreation(Order order)
{
// ...existing code...
}
// 9. 嵌套类型(如果有)
private class OrderValidationContext
{
public Order Order { get; set; }
public List<string> Errors { get; set; }
}
}
```
- 一个文件一个主要类原则
```csharp
// ✅ 建议:一个文件一个主要类
// filepath: Models/Order.cs
public class Order
{
// ...existing code...
}
// ❌ 不建议:一个文件多个不相关的类
// filepath: Models.cs
public class Order { }
public class Customer { }
public class Product { }
// ✅ 例外:紧密相关的小类可以放在一起
// filepath: Models/OrderEnums.cs
public enum OrderStatus
{
Pending,
Completed
}
public enum PaymentStatus
{
Unpaid,
Paid
}
```
## 2. 缩进与空格
- 使用 4 个空格缩进
```csharp
public class OrderService
{
public void ProcessOrder(Order order)
{
if (order != null)
{
var total = CalculateTotal(order);
order.TotalAmount = total;
}
}
}
```
- 运算符周围使用空格
```csharp
// ✅ 建议
var total = price * quantity;
var discount = total > 1000 ? 0.1m : 0;
var result = (a + b) * c;
var isValid = count > 0 && amount < maxAmount;
// 不建议
var total=price*quantity;
var result=(a+b)*c;
```
- 逗号后使用空格
```csharp
// 建议
public void CreateOrder(int customerId, decimal amount, string address)
{
var items = new List<int> { 1, 2, 3, 4 };
ProcessOrder(customerId, amount, address);
}
// ❌ 不建议
public void CreateOrder(int customerId,decimal amount,string address)
{
var items = new List<int> { 1,2,3,4 };
}
```
- 方法参数对齐
```csharp
// ✅ 短参数列表:单行
public Order CreateOrder(int customerId, decimal amount)
{
// ...existing code...
}
// ✅ 长参数列表:每个参数一行
public Order CreateOrder(
int customerId,
string customerName,
List<OrderItem> items,
string shippingAddress,
PaymentMethod paymentMethod,
decimal discountAmount)
{
// ...existing code...
}
// ✅ LINQ 链式调用对齐
var result = orders
.Where(o => o.IsActive)
.OrderByDescending(o => o.CreatedDate)
.Select(o => new OrderDto
{
OrderId = o.Id,
CustomerName = o.Customer.Name,
TotalAmount = o.TotalAmount
})
.ToList();
```
## 3. 大括号
- 大括号独占一行Allman 风格)
```csharp
// ✅ 建议:大括号独占一行
public void ProcessOrder(Order order)
{
if (order != null)
{
ValidateOrder(order);
SaveOrder(order);
}
else
{
throw new ArgumentNullException(nameof(order));
}
}
// ❌ 不建议K&R 风格(不推荐在 C# 中使用)
public void ProcessOrder(Order order) {
if (order != null) {
ValidateOrder(order);
}
}
```
- 单行语句也使用大括号
```csharp
// ✅ 建议:即使单行也使用大括号
if (order.IsValid)
{
ProcessOrder(order);
}
for (int i = 0; i < count; i++)
{
ProcessItem(i);
}
// 不建议省略大括号易出错可读性差
if (order.IsValid)
ProcessOrder(order);
LogOrder(order); // 这行不在 if 块内
```
## 4. 空行
- 方法之间使用空行
```csharp
public class OrderService
{
public Order GetOrder(int id)
{
return _repository.GetById(id);
}
// 空行分隔方法
public void CreateOrder(Order order)
{
_repository.Add(order);
}
// 空行分隔方法
public void UpdateOrder(Order order)
{
_repository.Update(order);
}
}
```
- 逻辑块之间使用空行
```csharp
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
// 验证输入
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
ValidateRequest(request);
// 空行分隔逻辑块
// 创建订单
var order = new Order
{
CustomerId = request.CustomerId,
CreatedDate = DateTime.Now
};
// 空行分隔逻辑块
// 保存并返回
await _repository.AddAsync(order);
await _unitOfWork.CommitAsync();
return order;
}
```
- 不要使用多个连续空行
```csharp
// ✅ 建议:使用单个空行
public void Method1()
{
// ...existing code...
}
public void Method2()
{
// ...existing code...
}
// ❌ 不建议:多个连续空行
public void Method3()
{
// ...existing code...
}
```
## 5. 语句换行
- 长条件表达式换行
```csharp
// ✅ 建议:在逻辑运算符前换行
if (customer.IsActive
&& customer.TotalOrders > 10
&& customer.TotalAmount > 10000
&& !customer.IsBlacklisted)
{
ApplyVipDiscount(customer);
}
// ✅ 复杂条件时可提取为变量
var isEligibleForDiscount = customer.IsActive
&& customer.TotalOrders > 10
&& customer.TotalAmount > 10000
&& !customer.IsBlacklisted;
if (isEligibleForDiscount)
{
ApplyVipDiscount(customer);
}
```
- LINQ 查询换行
```csharp
// ✅ 建议:每个操作符一行
var result = orders
.Where(o => o.IsActive)
.Where(o => o.TotalAmount > 1000)
.OrderByDescending(o => o.CreatedDate)
.Select(o => new OrderDto
{
Id = o.Id,
CustomerName = o.Customer.Name,
TotalAmount = o.TotalAmount
})
.ToList();
```
## 6. using 指令组织
- 移除未使用的 `using`
```csharp
// ✅ 建议:只保留使用的命名空间
using System;
using System.Linq;
using CompanyName.ProjectName.Core.Entities;
// ❌ 不建议:包含未使用的命名空间
using System;
using System.Collections.Generic; // 未使用
using System.Text; // 未使用
using System.Linq;
```
- 使用 `global using` (`C#` 10 +)
```csharp
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;
global using Microsoft.Extensions.Logging;
```
- `using static` 使用
```csharp
// ✅ 建议:简化静态成员调用
using static System.Math;
public class Calculator
{
public double CalculateArea(double radius)
{
return PI * Pow(radius, 2); // 无需 Math.PI 和 Math.Pow
}
}
// ❌ 不建议:过度使用会降低可读性
using static System.Console; // 不推荐WriteLine 来源不明确
```
## 7. 变量与数据类型
### 7.1 变量声明原则
- 就近声明原则
```csharp
// ✅ 建议:在使用前声明
public void ProcessOrder(int orderId)
{
var order = GetOrder(orderId);
if (order == null)
{
return;
}
// 在需要时才声明
var discount = CalculateDiscount(order);
order.DiscountAmount = discount;
// 在循环中声明
foreach (var item in order.Items)
{
var itemTotal = item.Price * item.Quantity;
ValidateItem(item, itemTotal);
}
}
// ❌ 不建议:过早声明
public void ProcessOrder(int orderId)
{
var order = GetOrder(orderId);
var discount = 0m; // 过早声明
var itemTotal = 0m; // 过早声明
if (order == null)
{
return;
}
discount = CalculateDiscount(order);
// ...existing code...
}
```
- 最小作用域原则
```csharp
// ✅ 建议:限制变量作用域
public decimal CalculateTotal(Order order)
{
decimal total = 0;
{
var taxRate = GetTaxRate();
var tax = order.Subtotal * taxRate;
total = order.Subtotal + tax;
}
// taxRate 和 tax 在此处不可访问
return total;
}
// ✅ 在 if 语句中声明
if (TryGetOrder(orderId, out var order))
{
ProcessOrder(order);
}
// order 在此处不可访问
```
- 初始化时声明
```csharp
// ✅ 建议:声明时初始化
var customerName = "张三";
var orderCount = 0;
var items = new List<OrderItem>();
var isValid = ValidateInput();
// ❌ 不建议:分离声明和初始化
string customerName;
int orderCount;
List<OrderItem> items;
customerName = "张三";
orderCount = 0;
items = new List<OrderItem>();
```
### 7.2 `null` 处理
- 使用可空类型
```csharp
// ✅ 建议:使用可空类型表示可能为 null 的值
public class Order
{
public int Id { get; set; }
public DateTime? CompletedDate { get; set; } // 可能未完成
public decimal? DiscountAmount { get; set; } // 可能无折扣
public string? Note { get; set; } // 可能无备注 (C# 8.0+)
}
```
- `null` 检查
```csharp
// ✅ 建议:参数 null 检查
public void ProcessOrder(Order order)
{
if (order == null)
{
throw new ArgumentNullException(nameof(order));
}
// 或使用 C# 11+ 参数 null 检查
public void ProcessOrder(Order order!!)
{
// order 不为 null
}
}
// ✅ 返回值 null 检查
var order = GetOrder(orderId);
if (order == null)
{
_logger.LogWarning("订单 {OrderId} 不存在", orderId);
return;
}
// ✅ 使用模式匹配
if (GetOrder(orderId) is Order order)
{
ProcessOrder(order);
}
```
- 使用 `?.``??` 操作符
```csharp
// ✅ null 条件运算符 ?.
var customerName = order?.Customer?.Name;
var itemCount = order?.Items?.Count ?? 0;
// ✅ null 合并运算符 ??
var discountAmount = order.DiscountAmount ?? 0m;
var shippingAddress = order.ShippingAddress ?? order.Customer.DefaultAddress;
// ✅ null 合并赋值运算符 ??=
order.Note ??= "无备注";
_cache ??= new Dictionary<string, object>();
// ✅ 链式使用
var cityName = order?.Customer?.Address?.City ?? "未知";
```
- 避免返回 `null`
```csharp
// ❌ 不建议:返回 null
public List<Order> GetOrders(int customerId)
{
var orders = _repository.GetByCustomerId(customerId);
return orders; // 可能返回 null
}
// ✅ 建议:返回空集合
public List<Order> GetOrders(int customerId)
{
var orders = _repository.GetByCustomerId(customerId);
return orders ?? new List<Order>();
}
// ✅ 建议:使用 LINQ
public IEnumerable<Order> GetOrders(int customerId)
{
return _repository.GetByCustomerId(customerId)
?? Enumerable.Empty<Order>();
}
```
- 使用 `nullable reference types` (`C#` 8.0+)
```csharp
#nullable enable
public class OrderService
{
// 不可为 null
private readonly IOrderRepository _repository;
// 可为 null
private ILogger? _logger;
public OrderService(IOrderRepository repository)
{
_repository = repository; // 必须赋值
}
public Order? GetOrder(int id) // 明确返回可能为 null
{
return _repository.GetById(id);
}
public void ProcessOrder(Order order) // 参数不可为 null
{
// order 保证不为 null
var total = order.TotalAmount;
}
}
```
### 7.3 字符串操作
- 字符转拼接
```csharp
// ❌ 不建议:循环中使用 + 拼接
string result = "";
for (int i = 0; i < 1000; i++)
{
result += i.ToString(); // 性能差产生大量临时对象
}
// 建议使用 StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
sb.Append(i);
}
string result = sb.ToString();
// 少量拼接可以使用字符串插值
string name = "张三";
int age = 25;
string message = $"姓名:{name},年龄:{age}"; // 推荐
// 使用 string.Join
var items = new[] { "苹果", "香蕉", "橙子" };
string result = string.Join(", ", items); // "苹果, 香蕉, 橙子"
```
- 字符串比较
```csharp
// 建议使用 StringComparison
if (string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase))
{
// 忽略大小写比较
}
// 检查 null 或空
if (string.IsNullOrEmpty(customerName))
{
throw new ArgumentException("客户名称不能为空");
}
if (string.IsNullOrWhiteSpace(note))
{
note = "无备注";
}
// 不建议使用 == 比较可能为 null 的字符串
if (str1 == str2) // 如果都为 null 会返回 true
{
// ...existing code...
}
```
- 字符串格式化
```csharp
// 推荐使用字符串插值
decimal price = 99.99m;
string message = $"价格:{price:C}"; // "价格:¥99.99"
// 格式化数字
int number = 1234567;
string formatted = $"{number:N0}"; // "1,234,567"
string hex = $"{number:X}"; // "12D687"
// 格式化日期
DateTime now = DateTime.Now;
string dateStr = $"{now:yyyy-MM-dd HH:mm:ss}"; // "2025-11-26 14:30:00"
string shortDate = $"{now:d}"; // "2025/11/26"
// 对齐和填充
string name = "张三";
string aligned = $"{name, 10}"; // 右对齐总宽度10
string leftAligned = $"{name, -10}"; // 左对齐
```
### 7.4 集合
- 集合初始化
```csharp
// 集合初始化器
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var dict = new Dictionary<string, int>
{
["apple"] = 1,
["banana"] = 2,
["orange"] = 3
};
// ✅ 指定初始容量(已知大小时)
var largeList = new List<int>(1000); // 避免多次扩容
// ✅ 数组初始化
int[] scores = { 85, 90, 78, 92 };
int[] grades = new int[5]; // 固定大小
```
- 集合选择
```csharp
// ✅ List<T> - 需要按索引访问和频繁添加
var customers = new List<Customer>();
// ✅ HashSet<T> - 需要唯一性检查和快速查找
var uniqueIds = new HashSet<int>();
uniqueIds.Add(1);
uniqueIds.Add(1); // 不会重复添加
// ✅ Dictionary<TKey, TValue> - 键值对存储和快速查找
var customerCache = new Dictionary<int, Customer>();
// ✅ Queue<T> - 先进先出
var taskQueue = new Queue<Task>();
// ✅ Stack<T> - 后进先出
var history = new Stack<string>();
// ✅ ImmutableList<T> - 不可变集合(线程安全)
var immutableList = ImmutableList.Create(1, 2, 3);
```
### 7.5 常量与魔法数字处理
- 常量定义
```csharp
// ✅ 定义常量类
public static class OrderConstants
{
public const int MaxItemsPerOrder = 100;
public const decimal MinOrderAmount = 50.0m;
public const int OrderTimeoutMinutes = 30;
// 字符串常量
public const string DefaultCurrency = "CNY";
public const string DateFormat = "yyyy-MM-dd";
}
// ✅ 在类中使用私有常量
public class OrderService
{
private const int MaxRetryCount = 3;
private const string LogPrefix = "[OrderService]";
public void ProcessOrder(Order order)
{
if (order.Items.Count > OrderConstants.MaxItemsPerOrder)
{
throw new BusinessException($"订单商品数量不能超过{OrderConstants.MaxItemsPerOrder}");
}
}
}
```
- 避免魔法数字
```csharp
// ❌ 不建议:魔法数字
public decimal CalculateDiscount(decimal amount)
{
if (amount > 1000)
{
return amount * 0.1m; // 0.1 是什么?
}
if (amount > 500)
{
return amount * 0.05m; // 0.05 是什么?
}
return 0;
}
// ✅ 建议:使用命名常量
public class DiscountCalculator
{
private const decimal HighAmountThreshold = 1000m;
private const decimal MediumAmountThreshold = 500m;
private const decimal HighDiscountRate = 0.1m;
private const decimal MediumDiscountRate = 0.05m;
public decimal CalculateDiscount(decimal amount)
{
if (amount > HighAmountThreshold)
{
return amount * HighDiscountRate;
}
if (amount > MediumAmountThreshold)
{
return amount * MediumDiscountRate;
}
return 0;
}
}
```
### 7.6 枚举类型
-
```csharp
public class OrderService
{
// ✅ 使用枚举而非字符串或数字
public void UpdateOrderStatus(int orderId, OrderStatus status)
{
// 类型安全,编译时检查
switch (status)
{
case OrderStatus.Pending:
// 处理待处理状态
break;
case OrderStatus.Confirmed:
// 处理已确认状态
break;
default:
throw new ArgumentException($"不支持的订单状态:{status}");
}
}
// ✅ 枚举转换字符串
public string GetStatusName(OrderStatus status)
{
return status.ToString(); // "Pending"
}
// ✅ 字符串转枚举(安全)
public OrderStatus ParseStatus(string statusText)
{
if (Enum.TryParse<OrderStatus>(statusText, out var status))
{
return status;
}
throw new ArgumentException($"无效的订单状态:{statusText}");
}
// ✅ 获取所有枚举值
public IEnumerable<OrderStatus> GetAllStatuses()
{
return Enum.GetValues<OrderStatus>();
}
// ✅ 标志枚举操作
public void ManagePermissions()
{
var permissions = FilePermissions.None;
// 添加权限
permissions |= FilePermissions.Read;
permissions |= FilePermissions.Write;
// 检查权限
bool canRead = permissions.HasFlag(FilePermissions.Read);
// 移除权限
permissions &= ~FilePermissions.Write;
}
}
```