Document

你可能忽视的 C# 性能优化细节

在日常开发中,C# 提供了优雅的语法和丰富的特性,让我们能够快速构建功能。但在高并发或大数据量场景下,某些“看似无害”的写法,可能暗中拖累性能。

本文总结了 C# 常见的性能陷阱,帮你快速避坑。


1. string 拼接

字符串在 C# 中是 不可变对象,每次拼接都会分配新的内存。

// ❌ 循环拼接 = O(n²) 复杂度
string result = "";
for (int i = 0; i < 1000; i++)
    result += i;

// ✅ StringBuilder 更高效
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
    sb.Append(i);
string result = sb.ToString();

📌 建议:

  • • 小规模拼接 → + 或字符串插值即可。
  • • 大规模循环拼接 → StringBuilder

2. LINQ vs for 循环

LINQ 简洁,但性能有代价:它会创建迭代器对象,并产生额外的方法调用。

// ❌ LINQ 在高频场景下可能偏慢
var sum = numbers.Where(x => x % 2 == 0).Sum();

// ✅ for 循环更高效
int sum = 0;
for (int i = 0; i < numbers.Length; i++)
    if (numbers[i] % 2 == 0) sum += numbers[i];

📌 建议:

  • • 性能敏感场景用 for / foreach
  • • 业务逻辑清晰优先时用 LINQ。

3. 装箱与拆箱

装箱就是把值类型(int、struct)转成 object,拆箱再转回来。

// ❌ 装箱/拆箱开销大
object obj = 42;   // 装箱
int x = (int)obj;  // 拆箱

// ✅ 用泛型避免装箱
List<int> list = new List<int>();
list.Add(42);

📌 建议:

  • • 高频操作尽量避免 object / 非泛型集合。
  • • 使用泛型(如 List<T>Dictionary<K,V>)。

4. 值类型 vs 引用类型

值类型(struct)分配在栈上,引用类型(class)在堆上。如果 struct 太大,频繁复制会拖慢性能。

// ❌ 大 struct 会导致频繁复制
struct BigStruct { public int A, B, C, D, E, F, G, H; }

// ✅ 用 class 或只定义小 struct
class BigClass { public int A, B, C, D, E, F, G, H; }

📌 建议:

  • • struct 小而轻(如 Point、Vector3)时使用。
  • • 大对象、复杂对象 → class。

5. 异常处理的滥用

异常的开销远大于 if 判断。

// ❌ 不要用异常代替逻辑控制
try
{
    int x = int.Parse("abc");
}
catch { }

// ✅ TryParse 更高效
if (int.TryParse("abc"out var value)) { }

📌 建议:

  • • 异常用于真正的异常场景。

6. async/await 的额外开销

async/await 会生成状态机对象,带来分配。

// ❌ 无意义 async
public async Task<intGetValueAsync() => 42;

// ✅ 用 ValueTask
public ValueTask<intGetValueAsync() => new(42);

📌 建议:

  • • 高频调用的短方法 → ValueTask
  • • 避免“无意义 async”。

7. 不合理的集合选择

不同集合适用场景不同。

集合类型 优势 不适合场景
List 随机访问快 中间频繁插入/删除
Dictionary<K,V> 查找 O(1) 小数据量开销大
LinkedList 插入/删除快 遍历慢
HashSet 去重/集合运算 顺序敏感场景
Queue/Stack FIFO/LIFO 高效 随机访问

8. 闭包(Closure)的隐形分配

闭包会导致捕获的变量提升到堆上。

// ❌ 产生闭包
int counter = 0;
Func<int> f = () => counter++;

// ✅ 避免闭包
int Increment(int x) => x + 1;

📌 建议:

  • • 高频循环避免创建闭包。

9. 大对象分配

大于 85KB 的对象会进 大对象堆,GC 成本高。

// ❌ 每次分配大数组
byte[] buffer = new byte[100_000];

// ✅ 用 ArrayPool
var buffer = ArrayPool<byte>.Shared.Rent(100_000);
ArrayPool<byte>.Shared.Return(buffer);

🔟 频繁调用 DateTime.Now

DateTime.Now 每次都会访问系统时钟。

// ❌ 高频调用
for (int i = 0; i < 1_000_000; i++)
    var t = DateTime.Now;

// ✅ 缓存一次
var now = DateTime.Now;

📌 建议:

  • • 高性能场景用 Stopwatch

1️⃣1️⃣ 滥用 ToList()

ToList() 会强制枚举并分配内存。

// ❌ 多余的 ToList()
var evens = numbers.Where(x => x % 2 == 0).ToList();

// ✅ 延迟执行更高效
foreach (var n in numbers.Where(x => x % 2 == 0))
    Console.WriteLine(n);

最后

性能优化不是“过早优化”,而是 避免无意识的性能陷阱
当应用规模变大、并发量上升时,这些小细节可能决定系统能否扛住压力。

🚀 记住:写代码先保证清晰易读,遇到瓶颈时再结合这些技巧优化。

posted @ 2025-09-12 10:42  从未被超越  阅读(13)  评论(0)    收藏  举报