.NET 中50种常见错误使用方法及推荐用法

科技   2024-11-06 06:05   上海  


下面是一个经过改进和扩展的列表,其中包含破坏 .NET 应用程序的 50 种方法,并解释了每种做法不佳的原因,以及演示如何解决每个问题的更正代码示例。这个全面的指南将有助于识别不良做法,并说明如何编写干净、可维护的代码。

1. 拖延综合症:延迟代码清理和重构

延迟清理和重构会积累技术债务。这会导致代码复杂、纠结,难以理解和维护,从而引入错误并减慢未来的开发速度。

public void ProcessOrder(Order order)  
{
if (order.Total > 1000) { /* complex business logic here */ }
// Complex code with quick fixes and nested logic.
// **TODO:** Refactor and clean up later.
}

修复

public class OrderProcessor  
{
public void ProcessOrder(Order order)
{
// Break down logic into smaller, reusable methods
ValidateOrder(order);
ApplyDiscounts(order);
SaveOrder(order);
}
private void ValidateOrder(Order order) { /* validation logic */ }
private void ApplyDiscounts(Order order) { /* discount logic */ }
private void SaveOrder(Order order) { /* database logic */ }
}

将代码分解为更小的自包含方法,并持续进行地址清理,以保持代码库的可管理性并减少技术债务。

2. 硬编码依赖项

硬编码的依赖关系使代码紧密耦合,难以测试且更难扩展。它可以防止轻松换出依赖项,从而降低灵活性。

public class OrderService  
{
private Database _database = new Database(); // Hardcoded dependency
public void PlaceOrder(Order order)
{
_database.Save(order);
}
}

修复

public class OrderService  
{
private readonly IDatabase _database;
public OrderService(IDatabase database)
{
_database = database;
}
public void PlaceOrder(Order order)
{
_database.Save(order);
}
}

通过构造函数注入依赖项,使代码更加灵活和可测试,遵循 Dependency Injection 模式。

3. 使用 或 阻止异步代码.Result.Wait()

阻止异步代码会导致死锁,尤其是在 ASP.NET 应用程序中。它还会阻止充分利用异步操作,从而降低性能。

public void ProcessData()  
{
var data = GetDataAsync().Result; // Blocking async call
}
public async Task<string> GetDataAsync()
{
await Task.Delay(1000);
return "Data";
}

修复

public async Task ProcessDataAsync()  
{
var data = await GetDataAsync(); // Proper async handling
}

使用而不是阻止像 .这使主线程可以自由用于其他任务,从而提高响应能力和可扩展性。await.Result

4. 捕获一般异常

捕获一般异常 () 会隐藏特定错误,从而难以适当地处理问题并有效地调试它们。Exception

try  
{
// Code that may throw
}
catch (Exception ex)
{
Console.WriteLine("Error occurred."); // Generic handling
}

修复

try  
{
// Code that may throw
}
catch (SqlException sqlEx)
{
Console.WriteLine("Database error: " + sqlEx.Message);
}
catch (IOException ioEx)
{
Console.WriteLine("File error: " + ioEx.Message);
}

捕获特定异常以更好地处理每种类型的错误并简化调试。

5. 不保护敏感数据

对 API 密钥或连接字符串等敏感信息进行硬编码会使其面临安全风险,使攻击者很容易访问这些数据。

public class DatabaseConfig  
{
public static string ConnectionString = "Server=myserver;User=myuser;Password=mypassword;";
}

修复

public class DatabaseConfig  
{
public static string ConnectionString => Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
}

将敏感信息存储在环境变量或 Azure Key Vault 等安全保管库中,以提高安全性并防止意外泄露。

6. 将业务逻辑放置在控制器中

将业务逻辑放在控制器中违反了关注点分离。随着逻辑的增长,它使代码更难维护、测试和扩展。

public IActionResult CreateOrder(Order order)  
{
if (order.Total > 1000) { /* special logic */ }
SaveToDatabase(order);
return Ok();
}

修复

public class OrderController : ControllerBase  
{
private readonly IOrderService _orderService;
public OrderController(IOrderService orderService)
{
_orderService = orderService;
}
public IActionResult CreateOrder(Order order)
{
_orderService.ProcessOrder(order);
return Ok();
}
}

使用服务层封装业务逻辑,使控制器只负责 HTTP 相关的操作。

7. 不一致的命名约定

不一致的命名约定会使代码更难阅读和维护,尤其是在团队环境中。这可能会导致混淆和错误。

public class orderService // Class names should be PascalCase  
{
public void SAVE_ORDER() // Method names should be PascalCase
{
int customerid = 1; // Variable names should be camelCase
}
}

修复

public class OrderService  
{
public void SaveOrder()
{
int customerId = 1;
}
}

遵循 .NET 命名约定:PascalCase 用于类和方法,camelCase 用于局部变量,以提高可读性和可维护性。

8. 违反 SOLID 原则

忽视 SOLID 原则会导致代码紧密耦合、僵化,难以扩展、测试和维护。

public class ReportService  
{
public void GenerateReport() { /*...*/ }
public void PrintReport() { /*...*/ } // Violates Single Responsibility
}

修复

public class ReportService  
{
public void GenerateReport() { /*...*/ }
}
public class ReportPrinter
{
public void PrintReport() { /*...*/ }
}

遵循单一责任原则,通过为不同的职责创建不同的类来分离关注点。

9. 使用内联查询直接 SQL 命令

内联 SQL 命令容易受到 SQL 注入攻击,并使查询更难维护和调试。

public void AddUser(string username)  
{
var query = $"INSERT INTO Users (Name) VALUES ('{username}')"; // SQL Injection risk
ExecuteQuery(query);
}

修复

public void AddUser(string username)  
{
var query = "INSERT INTO Users (Name) VALUES (@username)";
ExecuteQuery(query, new SqlParameter("@username", username));
}

使用参数化查询来避免 SQL 注入并提高安全性。参数化查询更安全且更易于维护。

10. 过度使用静态变量和方法

静态变量会创建全局状态,这可能会导致多线程环境中出现争用条件,从而使代码难以测试和调试。

public static class UserService  
{
public static int TotalUsers;
public static void AddUser() { TotalUsers++; }
}

修复

public class UserService  
{
public int TotalUsers { get; private set; }
public void AddUser()
{
TotalUsers++;
}
}

避免 state 的静态变量。请改用实例变量将状态保持在特定对象范围内,从而提高可测试性和线程安全性。

11. 跳过单元测试

跳过单元测试会导致代码更容易出现 bug,更难安全地重构,并且不太可靠。

// No unit tests are written

修复

public class OrderServiceTests  
{
[Fact]
public void PlaceOrder_ShouldSaveOrder_WhenOrderIsValid()
{
// Arrange
var orderService = new OrderService(new MockDatabase());
// Act
orderService.PlaceOrder(new Order());
// Assert
Assert.True(orderService.OrderSaved);
}
}

使用 xUnit 或 NUnit 等框架编写单元测试有助于及早发现错误,提高代码可靠性,并实现更安全的重构。

12. 使用魔术数字和字符串

硬编码值或“幻数”使代码更难阅读、理解和维护。如果需要更改值,它们也容易出错。

public void RetryOperation()  
{
for (int i = 0; i < 5; i++) // Magic number
{
// Retry logic
}
}

修复

private const int MaxRetries = 5;  
public void RetryOperation()
{
for (int i = 0; i < MaxRetries; i++)
{
// Retry logic
}
}

使用命名常量而不是幻数。这提高了可读性,并使代码更易于修改和理解。

13. 忽视正确的日志记录

依赖日志记录或根本不依赖日志记录会使调试和监视生产应用程序变得困难。日志对于识别问题和了解应用程序行为至关重要。Console.WriteLine

public void ProcessOrder()  
{
Console.WriteLine("Processing order."); // Basic logging
}

修复

private readonly ILogger<OrderService> _logger;  
public OrderService(ILogger<OrderService> logger)
{
_logger = logger;
}
public void ProcessOrder(Order order)
{
_logger.LogInformation("Processing order {OrderId} for customer {CustomerId}.", order.Id, order.CustomerId);
// Process the order
}

将结构化日志记录与 Serilog 或 Microsoft.Extensions.Logging 等框架结合使用,以记录有意义的结构化消息,这对于故障排除非常宝贵。

14. 过度使用 Global State

全局状态可能会导致意外的副作用、数据不一致,并且难以跟踪,尤其是在多线程环境中。

public static List<string> GlobalData = new List<string>();

修复

public class DataStore  
{
private readonly List<string> _data = new List<string>();
public void AddData(string item)
{
_data.Add(item);
}
}

使用 scoped 或特定于实例的状态。限制全局状态可降低数据意外更改的风险并提高可测试性。

15. 不使用依赖注入 (DI)

硬编码的依赖项使代码僵化、难以测试并限制了灵活性,尤其是当您需要在不同环境中交换依赖项时。

public class OrderProcessor  
{
private DatabaseService _dbService = new DatabaseService(); // Hardcoded dependency
}

修复

public class OrderProcessor  
{
private readonly IDatabaseService _dbService;
public OrderProcessor(IDatabaseService dbService)
{
_dbService = dbService;
}
}

使用 Dependency Injection 来解耦依赖关系,使代码更加灵活和可测试。

16. 忽略配置管理

硬编码的配置设置使代码不灵活,并且难以跨环境(例如,开发、测试、生产)进行修改。

public class MyService  
{
private string apiEndpoint = "https://api.example.com"; // Hardcoded config
}

修复

public class MyService  
{
private readonly IConfiguration _configuration;
public MyService(IConfiguration configuration)
{
_configuration = configuration;
}
public void Connect()
{
string apiEndpoint = _configuration["ApiSettings:Endpoint"];
// Use the endpoint
}
}

使用配置文件 (如 ) 或环境变量,它们允许在不修改代码的情况下更改设置。appsettings.json

17. 无法处理后台任务中的异常

后台任务中未经处理的异常可能会使应用程序以静默方式崩溃或使其处于不一致状态。

public async Task StartProcessingAsync()  
{
Task.Run(() => ProcessData()); // Unhandled exception risk
}

修复

public async Task StartProcessingAsync()  
{
Task.Run(async () =>
{
try
{
await ProcessData();
}
catch (Exception ex)
{
// Log or handle exception
}
});
}

始终通过将异常包装在块中来正确记录或处理错误,从而在后台任务中处理异常。try-catch

18. 使用已弃用或旧版软件包

旧版或已弃用的软件包通常缺乏安全更新和支持,这可能会导致兼容性问题和安全风险。

using System.Web; // Deprecated in .NET Core and .NET 5+

修复

using Microsoft.AspNetCore.Http; // Updated .NET Core library

定期更新依赖项并使用与最新 .NET 版本兼容的受支持的新式库,以确保您的应用程序安全且面向未来。

19. 忽略数据库连接池

每次创建新连接都会导致资源耗尽,这可能会降低性能并导致达到连接限制。

public void Connect()  
{
var connection = new SqlConnection("..."); // New connection each time
}

修复

public void Connect()  
{
using (var connection = new SqlConnection("..."))
{
connection.Open();
// Use connection
}
}

在数据库连接配置中启用连接池以重用连接并提高性能。

20. 未能实施适当的访问控制

硬编码的角色和权限使访问控制变得僵化且难以管理,从而增加了安全问题的风险。

[Authorize(Roles = "Admin")] // Hardcoded role

修复

[Authorize(Policy = "AdminPolicy")]

使用策略和基于声明的授权进行动态和可扩展的访问控制,轻松适应不断变化的需求。

21. 大型数据集处理不佳

将大型数据集加载到内存中可能会降低应用程序的速度或使其崩溃,尤其是在内存资源有限的情况下。

public List<User> GetAllUsers()  
{
return _dbContext.Users.ToList(); // Loads entire table into memory
}

修复

public List<User> GetUsers(int page, int pageSize)  
{
return _dbContext.Users
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList(); // Fetch only a subset
}

使用分页或延迟加载来高效处理大型数据集,从而提高性能并减少内存使用量。

22. 忽略依赖倒置原则

依赖关系反转是促进代码灵活性的 SOLID 原则的一部分。违反它会产生僵化的代码,从而降低可重用性和可测试性。

public class PaymentProcessor  
{
private CreditCardPayment _paymentMethod = new CreditCardPayment(); // Low-level module dependency
}

修复

public class PaymentProcessor  
{
private readonly IPaymentMethod _paymentMethod;
public PaymentProcessor(IPaymentMethod paymentMethod)
{
_paymentMethod = paymentMethod;
}
}

依赖于抽象 () 而不是具体的实现 (),从而提高灵活性并促进依赖反转原则。IPaymentMethodCreditCardPayment

23. 未正确使用 /asyncawait

不正确地使用 / 可能会导致性能瓶颈甚至死锁,尤其是在 UI 或 Web 应用程序中。asyncawait

public void SaveData()  
{
SaveToDatabaseAsync().Wait(); // Blocking async code
}

修复

public async Task SaveDataAsync()  
{
await SaveToDatabaseAsync(); // Proper async handling
}

用于正确处理异步代码,避免像那样阻止调用可能会导致性能和并发问题。await.Wait()

24. 直接在代码中使用DateTime.Now

在整个代码中直接使用使得测试依赖于时间的 logic 变得困难,并且使其依赖于系统的本地时间。DateTime.Now

public bool IsExpired(DateTime expirationDate)  
{
return DateTime.Now > expirationDate; // Direct use of DateTime.Now
}

修复

public interface IClock  
{
DateTime Now { get; }
}
public class SystemClock : IClock
{
public DateTime Now => DateTime.UtcNow;
}
public class ExpirationService
{
private readonly IClock _clock;
public ExpirationService(IClock clock)
{
_clock = clock;
}
public bool IsExpired(DateTime expirationDate)
{
return _clock.Now > expirationDate;
}
}

使用时间抽象,例如 ,可以在测试中模拟以模拟不同的时间,从而提高可测试性和一致性。IClock

25. 忽略对象的处理IDisposable

不处理对象(如数据库连接、文件流)可能会导致资源泄漏,随着时间的推移会降低应用程序性能。IDisposable

public void WriteData()  
{
var file = new StreamWriter("file.txt"); // Not disposed
file.WriteLine("Some data");
}

修复

public void WriteData()  
{
using (var file = new StreamWriter("file.txt")) // Proper disposal
{
file.WriteLine("Some data");
}
}

使用块或立即释放资源,防止资源泄漏并提高性能。usingDispose

26. 无处不在var

过度使用会降低代码的可读性,尤其是当变量的类型从上下文中不明显时。var

var users = GetUsers(); // What type is data?

修复

List<int> userIds= GetUsers(); // Explicit type improves readability

仅当从右侧清除类型时才使用。这增强了可读性,尤其是对于复杂类型。var

27. 忽略 Null 检查

不处理潜在的 null 值可能会导致运行时异常(例如 ),这些异常通常难以跟踪和调试。NullReferenceException

public void ProcessOrder(Order order)  
{
Console.WriteLine(order.Customer.Name); // Potential NullReferenceException
}

修复

public void ProcessOrder(Order order)  
{
if (order?.Customer?.Name != null)
{
Console.WriteLine(order.Customer.Name);
}
}

使用运算符或语句检查 null 值,这样可以提高代码安全性并避免意外崩溃。?.if

28. 使用大型方法

大型方法难以阅读、理解和维护。他们经常违反单一责任原则

public void ProcessOrder(Order order)  
{
// Hundreds of lines of code
}

修复

public void ProcessOrder(Order order)  
{
ValidateOrder(order);
ApplyDiscount(order);
SaveOrder(order);
}

将大型方法分解为更小、更集中的方法。这提高了可读性和可维护性,并使单元测试更容易。

29. 对常量使用魔术字符串

在整个代码中对字符串进行硬编码(例如,键或标识符)会增加拼写错误的风险,使重构变得困难,并降低可读性。

public void Configure(string option)  
{
if (option == "Production") { /*...*/ } // Magic string
}

修复

public static class Configuration  
{
public const string Production = "Production";
}
public void Configure(string option)
{
if (option == Configuration.Production) { /*...*/ }
}

使用常量或枚举而不是魔术字符串来减少错误并提高可读性。

30. 用于等待Thread.Sleep

使用块当前线程,这是浪费的,并且可能导致性能问题,尤其是在异步或 UI 应用程序中。Thread.Sleep

public void WaitForCompletion()  
{
Thread.Sleep(5000); // Blocking the thread
}

修复

public async Task WaitForCompletionAsync()  
{
await Task.Delay(5000); // Non-blocking
}

用于非阻塞等待,这样效率更高,尤其是在异步代码中。Task.Delay

31. 对控制流使用不适当的异常处理

使用异常来控制应用程序流效率低下且具有误导性,因为应为真正的异常情况保留异常。

public void ProcessOrder(string order)  
{
try
{
var parsedOrder = int.Parse(order); // Will throw if order is invalid
}
catch (Exception)
{
Console.WriteLine("Invalid order"); // Misusing exceptions for control flow
}
}

修复

public void ProcessOrder(string order)  
{
if (int.TryParse(order, out var parsedOrder))
{
Console.WriteLine("Order processed");
}
else
{
Console.WriteLine("Invalid order");
}
}

使用类似处理非异常情况的方法,这样效率更高,并避免将异常滥用于控制流。TryParse

32. 在循环中使用字符串连接

在循环中连接字符串效率低下,因为每次连接都会创建一个新的字符串对象,从而导致内存和性能问题。

public string GenerateMessage(List<string> items)  
{
string message = "";
foreach (var item in items)
{
message += item + ", "; // Inefficient concatenation
}
return message;
}

修复

public string GenerateMessage(List<string> items)  
{
var sb = new StringBuilder();
foreach (var item in items)
{
sb.Append(item).Append(", ");
}
return sb.ToString();
}

用于高效的字符串连接,尤其是在循环中,以减少内存开销。StringBuilder

33. 使用 static 类进行状态管理

使用静态类管理状态会引入全局状态,这可能会导致错误、争用条件,并使应用程序难以测试。

public static class StateManager  
{
public static int Count { get; set; }
}

修复

public class StateManager  
{
public int Count { get; set; }
}

使用实例类进行状态管理,从而更好地控制实例并降低与全局状态相关的风险。

34. 在主线程上执行 I/O 操作

在主线程上执行 I/O 操作(如文件或网络访问)会阻止它,从而导致 UI 冻结或 Web 应用程序延迟。

public void ReadFile()  
{
var content = File.ReadAllText("file.txt"); // Blocks the main thread
}

修复

public async Task ReadFileAsync()  
{
var content = await File.ReadAllTextAsync("file.txt"); // Non-blocking
}

使用异步方法(如执行 I/O 操作)而不阻塞,从而提高响应能力。ReadAllTextAsync

35. 使用复杂链而不是多态性if-else

长或链使代码更难理解和修改。在不修改整个链的情况下添加新的案例是很困难的。if-elseswitch

public decimal CalculateDiscount(string customerType)  
{
if (customerType == "Regular") return 0.1m;
else if (customerType == "Premium") return 0.2m;
else if (customerType == "VIP") return 0.3m;
return 0m;
}

修复

public interface ICustomerDiscount  
{
decimal GetDiscount();
}
public class RegularCustomerDiscount : ICustomerDiscount
{
public decimal GetDiscount() => 0.1m;
}
public class PremiumCustomerDiscount : ICustomerDiscount
{
public decimal GetDiscount() => 0.2m;
}
// Usage:
ICustomerDiscount discount = new PremiumCustomerDiscount();
discount.GetDiscount();

多态性替换复杂的条件逻辑,使代码更加灵活和可扩展。

36. 硬编码 URL 或端点

直接在代码中对 URL 或终端节点进行硬编码使得难以跨环境(例如开发、暂存、生产)更改它们,并增加意外暴露敏感终端节点的风险。

public class ApiService  
{
private string apiUrl = "https://api.example.com/v1"; // Hardcoded URL
}

修复

public class ApiService  
{
private readonly IConfiguration _configuration;
public ApiService(IConfiguration configuration)
{
_configuration = configuration;
}
public void Connect()
{
string apiUrl = _configuration["ApiSettings:Url"];
// Use the apiUrl
}
}

将 URL 存储在配置文件或环境变量中,无需更改代码即可针对不同环境轻松修改它们。

37. 内联编写复杂的 LINQ 查询

内联编写的复杂 LINQ 查询难以阅读、调试和维护。它们会掩盖逻辑并使代码更难接近。

var result = users.Where(u => u.Age > 18 && u.Name.StartsWith("A"))  
.OrderBy(u => u.Name)
.Select(u => new { u.Name, u.Age });

修复

var adultsWithNameStartingA = users  
.Where(IsAdult)
.Where(HasNameStartingWithA)
.OrderBy(u => u.Name)
.Select(u => new { u.Name, u.Age });
private static bool IsAdult(User u) => u.Age > 18;
private static bool HasNameStartingWithA(User u) => u.Name.StartsWith("A");

将复杂的 LINQ 查询分解为较小的方法或表达式。这种方法提高了可读性,使调试更容易。

38. 使用字符串实现类似 Enum 的行为

依赖特定值的字符串而不是枚举会增加拼写错误的风险,并使代码更难重构和验证。

public void ProcessOrder(string orderType)  
{
if (orderType == "Standard") { /* Process standard order */ }
else if (orderType == "Express") { /* Process express order */ }
}

修复

public enum OrderType { Standard, Express }  
public void ProcessOrder(OrderType orderType)
{
if (orderType == OrderType.Standard) { /* Process standard order */ }
else if (orderType == OrderType.Express) { /* Process express order */ }
}

对预定义类别使用枚举而不是字符串。枚举更安全,减少了错误,并使代码更易于理解和重构。

39. 忽略不可变字段的关键字readonly

如果字段应是不可变的(即初始化后未修改),则容易发生意外更改。readonly

public class Configuration  
{
public string ApiKey; // Missing readonly modifier
}

修复

public class Configuration  
{
public readonly string ApiKey;
public Configuration(string apiKey)
{
ApiKey = apiKey;
}
}

用于只应分配一次的字段。这可以防止意外修改并阐明不可变性的意图。readonly

40. 不用于只读数据IEnumerable

将集合公开为允许调用方修改它们,这可能会导致意外更改。这会破坏封装并可能引入 bug。List<T>

public List<Order> GetOrders()  
{
return _orders;
}

修复

public IEnumerable<Order> GetOrders()  
{
return _orders.AsReadOnly();
}

用于只读集合,以防止调用方进行修改,从而保护数据完整性并保留封装。IEnumerable<T>

41. 未能在方法名称中使用async

没有后缀的异步方法可能会使开发人员感到困惑,因为他们不会立即识别出该方法是异步的。Async

public async Task ProcessData()  
{
await Task.Delay(1000);
}

修复

public async Task ProcessDataAsync()  
{
await Task.Delay(1000);
}

按照约定将后缀添加到异步方法名称中,使代码更易于理解和遵循最佳实践。Async

42. 在构造函数之外直接修改属性

在构造函数或方法之外修改属性或字段可能会导致不可预知的行为和难以发现的错误。

public class Order  
{
public string OrderId { get; set; } = Guid.NewGuid().ToString(); // Set outside of constructor
}

修复

public class Order  
{
public string OrderId { get; }
public Order()
{
OrderId = Guid.NewGuid().ToString();
}
}

在构造函数中初始化属性,以保持实例化一致并防止意外修改。

43. 使用而不是通用列表ArrayList

ArrayList是非泛型的,并且缺乏类型安全性,这会增加由于向集合中添加无效类型而导致运行时错误的风险。

public ArrayList GetItems()  
{
ArrayList items = new ArrayList();
items.Add("item1");
items.Add(2); // Can lead to runtime errors
return items;
}

修复

public List<string> GetItems()  
{
List<string> items = new List<string> { "item1", "item2" };
return items;
}

使用泛型集合 (, , 等) 来确保类型安全并防止运行时错误。List<T>Dictionary<TKey, TValue>

44. 使用异常处理进行验证

异常成本高昂,只应用于异常情况。对验证或控制流使用异常会导致性能下降。

public void ProcessOrder(string orderId)  
{
try
{
ValidateOrder(orderId);
}
catch (Exception)
{
Console.WriteLine("Invalid order");
}
}

修复

public void ProcessOrder(string orderId)  
{
if (string.IsNullOrEmpty(orderId))
{
Console.WriteLine("Invalid order");
return;
}
// Process order
}

使用语句或自定义验证逻辑进行验证检查。为真正的异常情况保留例外,而不是控制流。if

45. 使用字段public

将字段公开为会破坏封装,并可能导致意外修改。它还限制了以后添加验证或 logic 的能力。public

public class Order  
{
public string OrderId; // Public field
}

修复

public class Order  
{
public string OrderId { get; private set; }
public Order(string orderId)
{
OrderId = orderId;
}
}

使用属性而不是公共字段来封装数据和控制访问,以便以后更轻松地进行修改和验证。

46. 不使用 for 属性名称nameof

将属性名称硬编码为字符串会使重构变得困难且容易出错,因为没有编译时检查。

public void Log(string message)  
{
Console.WriteLine("Error in ProcessData: " + message); // Hardcoded string
}

修复

public void Log(string message)  
{
Console.WriteLine($"Error in {nameof(ProcessData)}: {message}"); // Using nameof
}

用于引用属性或方法名称,这使得重构更安全,并防止由于字符串拼写错误而导致错误。nameof

47. 不使用字符串插值

与使用字符串插值相比,使用运算符连接字符串的可读性较差且更容易出错。+

public string GetMessage(string name)  
{
return "Hello, " + name + "! Welcome to our platform.";
}

修复

public string GetMessage(string name)  
{
return $"Hello, {name}! Welcome to our platform.";
}

使用字符串插值 () 实现更清晰、可读性的字符串格式,从而降低复杂字符串出错的风险。$"..."

48. 硬编码特定于区域性的格式

对特定于区域性的格式(例如,日期格式)进行硬编码可能会导致在不同区域设置或国际用户中产生不正确的结果。

public string FormatDate(DateTime date)  
{
return date.ToString("MM/dd/yyyy"); // U.S.-centric format
}

修复

public string FormatDate(DateTime date)  
{
return date.ToString("d", CultureInfo.InvariantCulture); // Culture-neutral formatting
}

对日期和数字使用非特定区域性格式 (),以确保不同区域之间的输出一致。InvariantCulture

49. 误用仅限日期的值DateTime

使用仅限日期的值通常会导致错误,因为时间组件可能会无意中影响计算或比较。DateTime

public bool IsToday(DateTime date)  
{
return date == DateTime.Today; // Might compare time as well
}

修复

public bool IsToday(DateTime date)  
{
return date.Date == DateTime.Today; // Use Date component only
}

用于在比较日期时忽略时间部分。在 .NET 6+ 中,请考虑使用 for 仅限日期的值。date.DateDateOnly

50. 不记录公共 API

缺乏有关公共方法和类的文档使其他开发人员难以理解如何使用代码,从而导致混淆和误用。

public void CalculateDiscount(decimal amount) { /*...*/ }

修复

/// <summary>
/// Calculates the discount based on the amount.
/// </summary>
/// <param name="amount">The amount to calculate the discount for.</param>
/// <returns>The discounted amount.</returns>
public decimal CalculateDiscount(decimal amount) { /*...*/ }

为公共 API 添加 XML 文档注释,以帮助其他开发人员了解方法和类的用途和用法。

这些示例说明了 .NET 开发中的各种陷阱,并为编写干净、可维护和高效的代码提供了解决方案。通过遵循这些最佳实践,您可以避免常见错误并创建更强大、更专业的应用程序。

如果你喜欢我的文章,请给我一个赞!谢谢

架构师老卢
资深软件架构师, 分享编程、软件设计经验, 教授前沿技术, 分享技术资源(每天发布电子书),每天进步一点点...
 最新文章