告别 .NET Framework 陈旧习惯!这 7 个现代 C# 实践让你的代码更高效、更安全

作者:微信公众号:【架构师老卢】
8-26 18:58
10

.NET Core 和 .NET 8+ 带来了全新的应用构建方式——但许多开发者(和团队)仍被困在 .NET Framework 的习惯中。

资历不等于现代化
某些代码能运行,并不代表它应该出现在你 2025 年的代码库里。

如果你一直在盲目复制资深同事的代码,可能会在不知不觉中导致应用性能更低、更难维护、更不安全。

让我们来解决这个问题。


1. 还在用 ConfigurationManager?这是遗留 .NET 的做法

你可能在这样写

var conn = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;

😬 问题所在

  • 默认不适用于现代 .NET
  • 不支持环境配置或依赖注入(DI)
  • 无法绑定到强类型类

应该这样做

public class ApiSettings
{
    public string ApiKey { get; set; }
}

// Startup 或 Program.cs
services.Configure<ApiSettings>(config.GetSection("ApiSettings"));

// 使用方式
public class MyService(IOptions<ApiSettings> settings)
{
    var apiKey = settings.Value.ApiKey;
}

现代配置 = 类型安全 + 环境感知 + 可注入。


2. 用 DateTime.Now 生成时间戳?这是在自找 Bug

旧习惯

var createdAt = DateTime.Now;

😵 为什么不好

  • 本地服务器时间 ≠ 全球统一时间
  • 在分布式系统中会出错
  • 夏令时令人头疼

现代 C# 的做法

var utc = DateTime.UtcNow;
var offset = DateTimeOffset.UtcNow;

仅在显示时转换为本地时间,切勿用于存储。
必要时使用 TimeZoneInfo——但所有时间都应以 UTC 存储。


3. 对异步方法调用 .Result?你正在阻塞线程

错误代码示例

var user = httpClient.GetStringAsync(url).Result;

😱 实际后果

  • 线程被阻塞
  • 可能发生死锁
  • 严重影响 ASP.NET Core 应用的可扩展性

彻底异步化

public async Task<IActionResult> GetUser()
{
    var result = await _api.GetUserAsync();
    return Ok(result);
}

如果你的方法调用了异步代码,请将其也改为异步。
不要和编译器对抗,让它赢。


4. 把所有服务都当作单例使用

新手操作

services.AddSingleton<DbContext>(); // ❌

💣 风险

  • DbContext 不是线程安全的
  • 会导致内存泄漏
  • 共享状态 = 共享 Bug

使用正确的生命周期

services.AddScoped<MyDbContext>();
services.AddTransient<IEmailSender, EmailSender>();
services.AddHttpClient<IMyApiClient, MyApiClient>();

💡 使用 HttpClientFactory,别再到处 new HttpClient() 了。


5. 用字符串拼接构建 SQL 或 URL

仍在这样写

var query = $"SELECT * FROM Users WHERE Name = '{name}'";

或:

var url = "https://api.com/users?name=" + name + "&city=" + city;

☠️ 潜在问题

  • SQL 注入
  • 编码错误
  • 维护噩梦

更安全、更智能的替代方案

// 使用 LINQ 或 EF Core
var users = await db.Users.Where(u => u.Name == name).ToListAsync();

// 使用 UriBuilder 构建 URL
var builder = new UriBuilder("https://api.com/users");
var query = HttpUtility.ParseQueryString("");
query["name"] = name;
query["city"] = city;
builder.Query = query.ToString();
var url = builder.ToString();

另外:在循环中始终使用 StringBuilder


6. 忽略可空引用类型

你的代码可能长这样

public string Email { get; set; } // 可能为 null 🤷‍♂️

💥 隐患

  • Null 是无声的杀手
  • 直到运行时才会发现问题
  • 现代 C# 可以避免空引用异常(NRE)

让编译器帮忙

<!-- 在 .csproj 中启用 -->
<Nullable>enable</Nullable>
public string Email { get; set; } = string.Empty;
public string? MiddleName { get; set; }

现在,当 Null 悄悄潜入时,编译器会发出警告。让它发挥作用。


7. 用异常控制流程

你会后悔的写法

try
{
    return int.Parse(input);
}
catch
{
    return 0; // 糟糕
}

或者更糟:

if (user == null)
    throw new UserNotFoundException();

💸 代价高昂

  • 异常处理速度慢
  • 适用于异常情况,而非预期逻辑
  • 让流程难以阅读

使用更智能的模式

// TryParse 模式
if (int.TryParse(input, out var value))
    return value;

// 可选返回或 Result 模式
public Result<User> GetUser(int id)
{
    var user = _db.Users.Find(id);
    return user != null 
        ? Result.Success(user) 
        : Result.Failure("User not found");
}

你不需要用 throw 来表示“未找到”。


✨ 超越“能运行的代码”

| ❌ 旧习惯 | ✅ 应该这样做 |
|----------|-------------|
| ConfigurationManager | 使用 DI 的 IOptions<T> |
| DateTime.Now | 使用 DateTimeOffset.UtcNow |
| .Result / .Wait() | 正确使用 async/await |
| 单例 DbContext | 使用 Scoped 或 Transient 生命周期 |
| 字符串拼接 SQL | 使用 LINQ / 参数化查询 |
| 可空类型混乱 | 启用可空引用类型 |
| 用异常控制逻辑 | 使用 TryParseResult<T> 模式 |


💡 最后一点建议

陈旧的习惯不属于现代应用。
如果你在团队的代码库中仍然看到这些做法,不要盲从——要带头改变。

资深开发者的价值不在于写了 10 万行代码,而在于知道什么不该写。

所以,停止这 7 种做法——即使你的技术负责人坚持相反的意见。

相关留言评论
昵称:
邮箱:
阅读排行