引言:为什么C#性能很重要
C#和.NET平台是构建高负载Web服务、桌面应用和Unity游戏的强大工具。然而,如果你不了解CLR(公共语言运行时)、垃圾回收器(GC)和JIT编译机制的工作方式,即使是最优雅的代码也可能运行缓慢。
本文将分析优化C#应用程序性能的关键方法,帮助你减少响应时间、降低内存消耗并提高系统吞吐量。所有建议均附有实用代码示例。
1. 内存管理和垃圾回收(GC)
.NET的主要特点之一是自动内存管理。但不正确地使用对象会导致频繁的垃圾回收,从而造成应用程序"暂停"(卡顿)。
1.1. 避免在热路径中分配内存
尽量不要在循环内部或频繁调用的方法中创建新对象。相反,应使用对象池或重用实例。
// 不好:每次迭代都会创建新的StringBuilderfor (int i = 0; i < 1000; i++){ var sb = new StringBuilder(); sb.Append(i);}
// 好:使用池或重用var sbPool = new StringBuilder();for (int i = 0; i < 1000; i++){ sbPool.Clear(); sbPool.Append(i);}1.2. 对于小型数据使用结构体而非类
结构体存储在栈上,不会给GC带来负担。但要注意:大型结构体(超过16字节)复制时可能较慢。
public struct Point{ public int X; public int Y;}
// 现在调用方法时不会在堆上创建对象void ProcessPoint(Point p) { /* ... */ }1.3. 为服务器应用程序启用服务器GC模式
默认情况下,控制台应用程序使用工作站GC。对于Web服务(ASP.NET Core),在csproj文件中切换到服务器GC:
<PropertyGroup> <ServerGarbageCollection>true</ServerGarbageCollection></PropertyGroup>2. 优化LINQ和集合的使用
LINQ是一个方便但可能较慢的工具,如果你不了解它的"陷阱"。
2.1. 避免多次枚举
每次调用.Count()、.ToList()或.Any()都可能重新执行查询。将结果保存到变量中。
// 不好:对集合进行两次遍历if (items.Where(x => x.IsActive).Count() > 0) { /* ... */ }var activeList = items.Where(x => x.IsActive).ToList();
// 好:一次遍历var activeItems = items.Where(x => x.IsActive).ToList();if (activeItems.Count > 0) { /* ... */ }2.2. 使用专用集合
对于频繁的基于键的查找,使用Dictionary<TKey, TValue>而不是List<T>。对于"先进先出"操作,使用Queue<T>。
// 快速查找:O(1) 而不是 O(n)var dict = new Dictionary<int, string>();dict.Add(1, "one");if (dict.ContainsKey(1)) { /* ... */ }2.3. 在关键部分用循环替代LINQ
对于简单操作(求和、过滤),for或foreach循环比LINQ快2-3倍。
// LINQ(慢)int sum = numbers.Sum();
// 循环(快)int sum = 0;for (int i = 0; i < numbers.Length; i++){ sum += numbers[i];}3. 异步编程和减少阻塞
正确使用async/await可以不阻塞线程,这对可扩展性至关重要。