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

21 KiB
Raw Blame History

1.2 代码风格


1. 文件组织结构

  • 类成员组织顺序

    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; }
        }
    }
    
  • 一个文件一个主要类原则

    // ✅ 建议:一个文件一个主要类
    // 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 个空格缩进

    public class OrderService
    {
        public void ProcessOrder(Order order)
        {
            if (order != null)
            {
                var total = CalculateTotal(order);
                order.TotalAmount = total;
            }
        }
    }
    
  • 运算符周围使用空格

    // ✅ 建议
    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;
    
  • 逗号后使用空格

    // ✅ 建议
    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 };
    }
    
  • 方法参数对齐

    // ✅ 短参数列表:单行
    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 风格)

    // ✅ 建议:大括号独占一行
    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);
        }
    }
    
  • 单行语句也使用大括号

    // ✅ 建议:即使单行也使用大括号
    if (order.IsValid)
    {
        ProcessOrder(order);
    }
    
    for (int i = 0; i < count; i++)
    {
        ProcessItem(i);
    }
    
    // ❌ 不建议:省略大括号(易出错、可读性差)
    if (order.IsValid)
        ProcessOrder(order);
        LogOrder(order);  // 这行不在 if 块内!
    

4. 空行

  • 方法之间使用空行

    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);
        }
    }
    
  • 逻辑块之间使用空行

    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;
    }
    
  • 不要使用多个连续空行

    // ✅ 建议:使用单个空行
    public void Method1()
    {
        // ...existing code...
    }
    
    public void Method2()
    {
        // ...existing code...
    }
    
    // ❌ 不建议:多个连续空行
    
    
    
    
    public void Method3()
    {
        // ...existing code...
    }
    

5. 语句换行

  • 长条件表达式换行

    // ✅ 建议:在逻辑运算符前换行
    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 查询换行

    // ✅ 建议:每个操作符一行
    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

    // ✅ 建议:只保留使用的命名空间
    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 +)

    global using System;
    global using System.Collections.Generic;
    global using System.Linq;
    global using System.Threading.Tasks;
    global using Microsoft.Extensions.Logging;
    
  • using static 使用

    // ✅ 建议:简化静态成员调用
    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 变量声明原则

  • 就近声明原则

    // ✅ 建议:在使用前声明
    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...
    }
    
  • 最小作用域原则

    // ✅ 建议:限制变量作用域
    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 在此处不可访问
    
  • 初始化时声明

    // ✅ 建议:声明时初始化
    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 处理

  • 使用可空类型

    // ✅ 建议:使用可空类型表示可能为 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 检查

    // ✅ 建议:参数 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);
    }
    
  • 使用 ?.?? 操作符

    // ✅ 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

    // ❌ 不建议:返回 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+)

    #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 字符串操作

  • 字符转拼接

    // ❌ 不建议:循环中使用 + 拼接
    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); // "苹果, 香蕉, 橙子"
    
  • 字符串比较

    // ✅ 建议:使用 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...
    }
    
  • 字符串格式化

    // ✅ 推荐使用字符串插值
    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 集合

  • 集合初始化

    // ✅ 集合初始化器
    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]; // 固定大小
    
  • 集合选择

    // ✅ 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 常量与魔法数字处理

  • 常量定义

    // ✅ 定义常量类
    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}");
            }
        }
    }
    
  • 避免魔法数字

    // ❌ 不建议:魔法数字
    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 枚举类型

  • 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;
        }
    }