详情

首页手游攻略 C#减少代码运行时间的7个实践技巧整理实用指南

C#减少代码运行时间的7个实践技巧整理实用指南

佚名 2026-09-09 17:00:01

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“C#减少代码运行时间的7个实践技巧整理”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

目录
  • 引言
  • 一、选对数据结构:O(n) vs O(1)
    • 错误示范
    • 正确做法
    • 性能对比
  • 二、字符串拼接:告别 String,拥抱 StringBuilder
    • 为什么 String 慢
    • StringBuilder 登场
    • 性能对比
  • 三、Span:零拷贝内存操作
    • 什么是 Span
    • 传统方式 vs Span
    • 实战案例:解析数字
    • 适用场景
  • 四、异步编程:不要阻塞线程
    • 错误示范:同步阻塞
    • 正确做法:async/await
    • 并行处理多个任务
  • 五、LINQ 陷阱:延迟执行 vs 立即执行
    • 延迟执行的坑
    • 多次枚举的性能问题
    • 性能对比
  • 六、避免装箱拆箱
    • 什么是装箱拆箱
    • 错误示范
    • 正确做法
    • 性能差异
  • 七、对象池:复用而非重建
    • 为什么需对象池
    • 采用 ArrayPool
    • 采用 MemoryPool
    • 适用场景
  • 总结:7 个优化清单
    • 写在最后

      引言

      实际处理时,C# 再次登顶!2025 年 TIOBE 年度编程语言榜单揭晓,C# 摘得桂冠。这已经是 C# 三年内第二次获此殊荣。

      但语言再好,代码写得慢也是白搭。

      你是否遇到过这样的场景:

      • 循环处理 10 万条数据,跑了几分钟
      • 字符串拼接内存爆炸
      • LINQ 查询看似优雅,实则性能堪忧

      今天我们就来聊聊 7 个实战技巧,让你的 C# 代码运行时间大幅缩短。

      一、选对数据结构:O(n) vs O(1)

      错误示范

      // 用 List 查找,时间复杂度 O(n)
      var list = new List<int> { 1, 2, 3, /* ...10万条 */ };
      if (list.Contains(99999)) { /* ... */ }

      正确做法

      // 用 HashSet 查找,时间复杂度 O(1)
      var set = new HashSet<int> { 1, 2, 3, /* ...10万条 */ };
      if (set.Contains(99999)) { /* ... */ }

      性能对比

      操作ListHashSetDictionary
      查找O(n)O(1)O(1)
      插入O(1)O(1)O(1)
      删除O(n)O(1)O(1)

      经验法则

      • 频繁查找 → HashSet / Dictionary
      • 需索引访问 → List
      • 需排序 → SortedSet / SortedDictionary

      二、字符串拼接:告别 String,拥抱 StringBuilder

      为什么 String 慢

      String 是不可变对象。每次拼接都会新建新对象:

      string s = "a";
      s += "b"; // 创建新对象 "ab",旧对象 "a" 等待 GC
      s += "c"; // 创建新对象 "abc",旧对象 "ab" 等待 GC

      循环拼接 10000 次?恭喜,你新建了 10000 个字符串对象。

      StringBuilder 登场

      var sb = new StringBuilder();
      for (int i = 0; i < 10000; i++)
      {
          sb.Append(i);
      }
      string result = sb.ToString();

      性能对比

      // 测试:拼接 10000 次字符串

      // String: 约 150ms
      string s = "";
      for (int i = 0; i < 10000; i++) s += i;

      // StringBuilder: 约 1ms
      var sb = new StringBuilder();
      for (int i = 0; i < 10000; i++) sb.Append(i);

      150 倍性能差距,这就是选对工具的力量。

      三、Span:零拷贝内存操作

      什么是 Span

      Span 是 C# 7.2 引入的高性能内存结构,它:

      • 指向连续内存区域
      • 无需复制数据
      • 栈分配,无 GC 压力

      传统方式 vs Span

      // 传统方式:截取子字符串会分配新内存
      string text = "Hello, ManongGangzi!";
      string sub = text.Substring(0, 5); // 新分配 "Hello"

      // Span 方式:零分配
      ReadOnlySpan<char> span = text.AsSpan();
      ReadOnlySpan<char> subSpan = span.Slice(0, 5); // 无分配

      实战案例:解析数字

      // 传统方式
      string numStr = "12345";
      int num = int.Parse(numStr); // 已分配的字符串

      // Span 方式
      ReadOnlySpan<char> span = "12345".AsSpan();
      int num = int.Parse(span); // 直接解析,无需中间字符串

      适用场景

      • 高频字符串处理
      • 大数组切片操作
      • 网络数据包解析
      • 游戏开发中的缓冲区操作

      四、异步编程:不要阻塞线程

      错误示范:同步阻塞

      // 阻塞主线程
      var result = httpClient.GetStringAsync(url).Result;

      这会导致:

      • 线程池饥饿
      • 应用响应变慢
      • 死锁风险

      正确做法:async/await

      // 非阻塞,释放线程
      public async Task<string> GetDataAsync(string url)
      {
          return await httpClient.GetStringAsync(url);
      }

      并行处理多个任务

      // 串行:总时间 = sum(每个任务时间)
      var data1 = await GetDataAsync(url1);
      var data2 = await GetDataAsync(url2);
      var data3 = await GetDataAsync(url3);

      // 并行:总时间 = max(每个任务时间)
      var tasks = new[]
      {
          GetDataAsync(url1),
          GetDataAsync(url2),
          GetDataAsync(url3)
      };
      var results = await Task.WhenAll(tasks);

      3 个请求各 1 秒

      • 串行:3 秒
      • 并行:1 秒

      五、LINQ 陷阱:延迟执行 vs 立即执行

      延迟执行的坑

      var numbers = new List<int> { 1, 2, 3 };

      var query = numbers.Where(n => n > 1); // 此时未执行

      numbers.Add(4); // 修改源数据

      var result = query.ToList(); // 这里才执行!
      // result = [2, 3, 4],包含了后添加的 4

      多次枚举的性能问题

      var query = numbers.Where(n => n > 1);

      // 错误:每次都重新执行
      if (query.Any()) // 执行一次
      {
          var first = query.First(); // 又执行一次
          var count = query.Count(); // 再执行一次
      }

      // 正确:缓存结果
      var list = numbers.Where(n => n > 1).ToList();
      if (list.Any())
      {
          var first = list.First(); // 从缓存读取
          var count = list.Count(); // 从缓存读取
      }

      性能对比

      操作延迟执行立即执行 (ToList)
      多次访问每次重新计算一次计算,多次读取
      内存占用需存储结果
      适用场景单次遍历多次访问

      六、避免装箱拆箱

      什么是装箱拆箱

      int value = 42;
      object obj = value; // 装箱:值类型 → 引用类型
      int back = (int)obj; // 拆箱:引用类型 → 值类型

      每次装箱都会:

      • 在堆上分配内存
      • 触发 GC

      错误示范

      var list = new ArrayList();  // 非泛型集合
      list.Add(1); // 装箱
      list.Add(2); // 装箱
      int sum = 0;
      foreach (int i in list) // 拆箱
      {
          sum += i;
      }

      正确做法

      var list = new List<int>();  // 泛型集合
      list.Add(1); // 无装箱
      list.Add(2); // 无装箱
      int sum = 0;
      foreach (int i in list) // 无拆箱
      {
          sum += i;
      }

      性能差异

      循环 100 万次:

      • ArrayList:约 50ms
      • List:约 5ms

      10 倍差距,这就是泛型的威力。

      七、对象池:复用而非重建

      为什么需对象池

      频繁新建销毁对象会:

      • 增加 GC 压力
      • 触发频繁 GC 暂停
      • 影响应用响应时间

      采用 ArrayPool

      using System.Buffers;

      // 传统方式:每次分配新数组
      byte[] buffer = new byte[1024];
      ProcessData(buffer);

      // 对象池方式:复用数组
      var pool = ArrayPool<byte>.Shared;
      byte[] buffer = pool.Rent(1024);
      try
      {
          ProcessData(buffer);
      }
      finally
      {
          pool.Return(buffer); // 归还池中
      }

      采用 MemoryPool

      using System.Buffers;

      var pool = MemoryPool<byte>.Shared;
      using var memoryOwner = pool.Rent(1024);
      ProcessData(memoryOwner.Memory);
      // 自动归还

      适用场景

      • 高频临时数组
      • 网络缓冲区
      • 游戏中的临时对象
      • 图像处理中的像素缓冲

      总结:7 个优化清单

      技巧收益适用场景
      选对数据结构O(n) → O(1)频繁查找
      StringBuilder150x 提升字符串拼接
      Span零拷贝内存操作
      async/await并行加速I/O 操作
      LINQ ToList避免重复计算多次访问
      泛型集合10x 提升避免装箱
      对象池减少 GC高频新建

      写在最后

      性能优化不是一蹴而就的,而是点滴积累

      记住三个原则:

      1. 先测量,再优化 —— 用 BenchmarkDotNet 找到真正的瓶颈
      2. 选对工具 —— 数据结构、API、设计模式
      3. 避免过早优化 —— 可读性优先,性能瓶颈出现时再优化

      到此这篇关于C#减少代码运行时间的7个实战技巧的文章就介绍到这了,更多相关C#减少代码运行时间内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!

      您可能感兴趣的文章:

      • C# Stopwatch实现计算代码运行时间
      • C#判断程序是否是管理员权限运行的方法示例代码
      • C#中用管理员身份运行程序代码实例
      • C#默认以管理员身份运行程序实现代码
      • c#防止多次运行代码收集分享
      • 优雅又实用的C#代码优化技巧分享

      相关资讯
      点击查看更多
      游戏推荐
      推荐专题
      热门阅读
      推荐下载