在简洁、现代的.NET开发中,有些C#特性至关重要,忽视它们无疑暴露了经验的欠缺。这些并非花哨的新玩具,而是能立即使初级代码与高级代码拉开差距的实用工具。如果你仍在用老方法做事,那么资深开发者对你投来异样目光时,可别感到惊讶。
下面我们来看看5个表明你的C#代码需要“升级”的明显迹象。
初级做法:
builder.Services.AddScoped<IMyService, MyService>();
// 未配置任何验证
只有在运行时(通常是在处理请求时),你才会发现配置有误。
高级做法:
var builder = Program.CreateHostBuilder(args);
builder.Services.AddScoped<IMyService, MyService>();
builder.Host.UseDefaultServiceProvider(
(context, options) =>
{
options.ValidateOnBuild = true;
options.ValidateScopes = true;
});
这两行代码能确保你在应用启动时就发现配置错误的服务,而不是等到生产环境中才暴露。ValidateScopes
可检测无效的生命周期(例如将Scoped服务注入到Singleton中),ValidateOnBuild
则强制容器提前尝试构造服务。如果存在问题,在应用运行之前你就能知晓。
初级思维:
builder.Services.AddTransient<UserSession>();
没有上下文说明,没有解释,也不考虑对象应该何时创建、何时销毁。
高级思维过程:
创建自定义作用域有助于手动隔离上下文:
using var scope = serviceProvider.CreateScope();
var scopedService = scope.ServiceProvider.GetRequiredService<IScopedThing>();
资深开发者清楚何时需要服务隔离,比如针对每个用户、每个请求或每个后台任务。
初级失误:
DateTime orderTime = DateTime.Now; // 本地时间
一旦用户处于不同时区,或者服务器调整时钟,这段代码就会出问题。
高级做法:
DateTimeOffset orderTime = DateTimeOffset.UtcNow;
DateTimeOffset
既包含时间戳,也包含偏移量,非常适合日志、交易和调度等实际场景。存储时用UTC时间,展示时转换为本地时间。不要与时间作对,而要尊重它。
杂乱版本:
System.Collections.Generic.Dictionary<System.Tuple<string, int>, List<MyNamespace.Models.ComplexThing>> myMap;
更简洁、更易维护的版本:
using ComplexMap = System.Collections.Generic.Dictionary<(string, int), List<ComplexThing>>;
别名不仅能缩短名称,还能明确意图。在重构或替换外部库时尤其有用,因为你只需修改别名这一行代码。
陈旧且冗长的方式:
var list = new List<int> { 1, 2, 3 };
更智能的方式(C# 12及以上版本):
List<int> list = [1, 2, 3];
更棒的是,你可以通过声明的类型来控制创建的集合类型,而不是由括号内的内容决定。
ISet<string> tags = ["blog", "dotnet"];
Queue<int> queue = [1, 2, 3];
这种内在的灵活性让你无需修改初始化逻辑就能轻松切换集合类型,而这一技巧很多初级开发者完全没掌握。
要脱颖而出,你不必记住晦涩的语法,只需善用现代C#提供的特性。从专业地处理依赖注入,到避免常见的日期时间bug,再到编写更简洁的集合代码,这些特性并非可有可无,而是必备技能。
这五个技巧并非什么秘密,却常常被忽视。开始使用它们吧,你不仅能写出更好的代码,还能培养系统工程师的思维,而你的软件也会因此更出色。