name: csharpdescription: “Language-specific super-code guidelines for csharp.”risk: safesource: communitydate_added: “2026-06-16”C#惯用高效写法参考何时使用当任务符合以下描述时使用此技能针对 csharp 的语言特定 super-code 指南。目录LINQ 与集合空值处理异步/等待记录与模式匹配错误处理资源管理C# 特有的反模式1. LINQ 与集合 {#linq}// ❌ 命令式累加varresultnewListstring();foreach(variteminitems){if(item.IsActive)result.Add(item.Name.ToUpper());}// ✅varresultitems.Where(ii.IsActive).Select(ii.Name.ToUpper()).ToList();// ❌ 手动分组vargroupednewDictionarystring,ListItem();foreach(variteminitems){if(!grouped.ContainsKey(item.Category))grouped[item.Category]newListItem();grouped[item.Category].Add(item);}// ✅vargroupeditems.GroupBy(ii.Category).ToDictionary(gg.Key,gg.ToList());// ❌ 先 Any() 再 First()if(items.Any(ii.IsValid)){varfirstitems.First(ii.IsValid);}// ✅varfirstitems.FirstOrDefault(ii.IsValid);if(firstisnotnull){...}2 个以上操作的链式调用优先使用方法语法。查询语法适合复杂 join。2. 空值处理 {#nulls}// ❌ 嵌套空值检查stringcitynull;if(user!nulluser.Address!null){cityuser.Address.City;}// ✅varcityuser?.Address?.City;// ❌ 用三元表达式做空值回退varnameuser!null?user.Name:Unknown;// ✅varnameuser?.Name??Unknown;// ❌ 事件调用前做空值检查if(OnChanged!null)OnChanged(this,args);// ✅OnChanged?.Invoke(this,args);// ❌ 手动抛出 ArgumentNullExceptionif(namenull)thrownewArgumentNullException(nameof(name));// ✅ (C# 10)ArgumentNullException.ThrowIfNull(name);在项目范围内启用可空引用类型Nullableenable/Nullable。3. 异步/等待 {#async}// ❌ 阻塞异步代码varresultGetDataAsync().Result;// 死锁风险// ✅varresultawaitGetDataAsync();// ❌ async void异常无法观察asyncvoidOnButtonClick(){awaitDoWork();}// ✅ — async Task只有确实需要的事件处理程序才用 async voidasyncTaskOnButtonClick(){awaitDoWork();}// ❌ 独立工作却顺序 awaitvaraawaitFetchA();varbawaitFetchB();// ✅var(a,b)(awaitTask.WhenAll(FetchA(),FetchB()))switch{varr(r[0],r[1])};// 或用 ValueTuple 更清晰vartaskAFetchA();vartaskBFetchB();varaawaittaskA;varbawaittaskB;// ❌ 在库内部用 Task.Run 包装同步代码publicTaskintGetValue()Task.Run(()ComputeSync());// ✅ — 让调用方决定暴露同步方法publicintGetValue()ComputeSync();库代码中添加ConfigureAwait(false)。应用/UI 代码中省略。4. 记录与模式匹配 {#records}// ❌ 为数据类型手写相等比较、ToString、DeconstructclassPoint{publicintX{get;init;}publicintY{get;init;}// Equals, GetHashCode, ToString...}// ✅ (C# 9)recordPoint(intX,intY);// ❌ 用 if-else 链做类型检查if(shapeisCircle){varc(Circle)shape;returnc.Radius*c.Radius*Math.PI;}elseif(shapeisRectangle){...}// ✅returnshapeswitch{Circle{Radius:varr}r*r*Math.PI,Rectangle{Width:varw,Height:varh}w*h,_thrownewArgumentException($Unknown shape:{shape})};// ❌ 用 做范围检查if(score0score100){...}// ✅ (C# 9)if(scoreis0and100){...}5. 错误处理 {#errors}// ❌ 捕获 Exception 只为记日志并吞掉try{Process();}catch(Exceptionex){logger.LogError(ex,error);}// ✅ — 捕获具体类型无法处理就重新抛出try{Process();}catch(HttpRequestExceptionex){thrownewServiceException(upstream failure,ex);}// ❌ throw ex重置堆栈跟踪catch(Exceptionex){throwex;}// ✅catch(Exceptionex){throw;}// 保留堆栈跟踪// 或包装throw new AppException(context, ex);// ❌ 用异常控制流程try{returndict[key];}catch(KeyNotFoundException){returndefaultValue;}// ✅returndict.TryGetValue(key,outvarvalue)?value:defaultValue;6. 资源管理 {#resources}// ❌ 手动 DisposevarconnnewSqlConnection(cs);conn.Open();// ... 使用 conn ...conn.Dispose();// 异常时遗漏// ✅usingvarconnnewSqlConnection(cs);conn.Open();// 作用域结束时自动释放// ❌ 冗长的 using 块using(varreadernewStreamReader(path)){returnreader.ReadToEnd();}// ✅ (C# 8)usingvarreadernewStreamReader(path);returnreader.ReadToEnd();// 或者直接return File.ReadAllText(path);7. C# 特有的反模式 {#antipatterns}反模式推荐做法string.Format({0}, x)${x}字符串插值公共 API 返回ListTIReadOnlyListT或IEnumerableTasync voidasync Task对 Task 使用.Result/.Wait()awaitthrow exthrow保留堆栈跟踪为数据类型手写IEquatablerecordobject参数带约束的泛型DateTime.NowDateTime.UtcNow或DateTimeOffset可变的公共字段带{ get; set; }或{ get; init; }的属性catch (Exception) { }吞掉一切捕获具体异常未知异常重新抛出没有using的IDisposableusing声明局限性这些是语言特定指南不涵盖整体架构决策。过度压缩可能降低可读性请酌情判断。