避免在循环里反复创建对象
频繁的堆分配会触发 GC,尤其在高频调用路径(如游戏帧更新、网络包解析)中,
new List<t>()</t>或
new StringBuilder()放在循环内是典型性能雷区。
实操建议:
把对象声明提到循环外,复用实例(注意线程安全——若非并发场景,直接复用;若多线程,考虑ThreadLocal<t></t>或对象池) 对短生命周期字符串拼接,优先用
string.Concat()或
string.Join(),而非循环中
+=用
Span<t></t>替代
ArraySegment<t></t>或临时数组,避免堆分配(例如解析字节流时用
Span<byte>.Slice()</byte>)
慎用 LINQ 方法链
Where().Select().ToList()看起来简洁,但每层都生成新迭代器 + 多次遍历 + 额外装箱(对值类型),在热路径中开销明显。
实操建议:
简单过滤/映射直接写 for 循环:索引访问快、无委托调用开销、无状态机生成 必须用 LINQ 时,优先选AsEnumerable()前的原生集合方法(如
List<t>.FindAll()</t>、
.ConvertAll()),它们是泛型实现,无装箱
Count()别用于已知长度的集合——直接用
.Count属性;
Any()比
Count() > 0快得多
减少装箱和拆箱
对
int、
DateTime等值类型调用
object.ToString()、存入
ArrayList或传给接受
object的旧 API,都会触发装箱——分配堆内存并拷贝值。
实操建议:
用泛型集合替代非泛型:List<int></int>而非
ArrayList,
Dictionary<string int></string>而非
Hashtable日志或调试输出时,避免隐式装箱:用
$"Value: {i}"(编译为 string.Format重载)比
"Value: " + i更优(后者触发
i.ToString()装箱) 自定义结构体上别轻易加虚方法或实现接口(除非必要),否则传参/赋值可能隐式装箱
异步 I/O 不要盲目用 Task.Run
把同步文件读取、JSON 解析等 CPU 密集操作包进
Task.Run(() => {...}),看似“异步”,实则只是把线程池线程占住,还增加调度开销,对吞吐无益反损。
实操建议:
I/O 密集操作(HTTP 请求、数据库查询、文件读写)优先用真正的异步方法:HttpClient.GetAsync()、
FileStream.ReadAsync()、
JsonSerializer.DeserializeAsync()CPU 密集任务(图像处理、加密计算)才考虑
Task.Run,且需评估是否真需要并行——有时单线程 SIMD 指令(如
Vector<t></t>)更高效 避免在
async void方法里做耗时工作;不要用
.Result或
.Wait()阻塞异步任务,易死锁
var stream = File.OpenRead("data.json");
// ✅ 正确:真正异步反序列化
var obj = await JsonSerializer.DeserializeAsync<MyData>(stream);
// ❌ 错误:同步读+阻塞式反序列化+额外线程调度
var bytes = await File.ReadAllBytesAsync("data.json");
var obj = JsonSerializer.Deserialize<MyData>(bytes); // 同步CPU密集操作
实际优化时,先用 dotnet-trace或 Visual Studio Profiler 抓热点,再针对性改。很多“技巧”在低频逻辑里毫无意义,反而让代码难懂。
