C#应用程序性能优化:初学者指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为何性能从代码的第一行就至关重要



每位开发者,即使是初学者,都会遇到这样的情况:一个简单的应用开始变慢、消耗过多内存或处理请求耗时过长。优化 C# 应用的性能并非魔法,也不是资深开发者的专属领域。这是一套可以在任何学习阶段掌握的实用规则和工具。



本文将深入解析优化 C# (.NET) 代码的关键方面。您将学习如何避免常见的初学者错误,加速处理内存、字符串和集合,并正确使用多线程。本文面向已熟悉 C# 语法基础并希望编写更高效代码的开发者。



1. 字符串处理:为何 String 不好而 StringBuilder 好



C# 中最常见的性能问题之一是错误地处理字符串。.NET 中的字符串是不可变的。这意味着任何连接操作都会在内存中创建一个新的字符串对象,而将旧对象留给垃圾回收器处理。



问题:循环中的连接



考虑一个不好的例子:



string result = "";for (int i = 0; i < 1000; i++){    result += i.ToString() + " ";}// 这创建了 1000 个中间字符串!


每次循环迭代都会创建一个新字符串。对于 1000 次迭代,那就是 1000 次内存分配。如果是 100,000 次迭代,应用可能会冻结或抛出 OutOfMemoryException。



解决方案:使用 StringBuilder



StringBuilder 类使用可变的字符缓冲区。每次添加文本时,它不会创建新对象。



using System.Text;

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


提示: 如果需要执行超过 3-5 次连接操作,尤其是在循环内部,始终使用 StringBuilder。对于简单的几个字符串连接(例如,string s = a + b + c;),C# 编译器会将其优化为一次 string.Concat 调用,因此这里不需要 StringBuilder。



2. 优化 LINQ:选择正确的方法



LINQ(语言集成查询)是一个强大的工具,但错误使用可能会显著降低应用速度。主要问题是对集合的多次枚举。



"昂贵" LINQ 查询的示例



var numbers = Enumerable.Range(1, 1000000);var countEven = numbers.Where(n => n % 2 == 0).Count();// 这里 Where() 创建了一个迭代器,Count() 对其进行遍历。// 如果集合只被枚举一次,这是可以的。


在同一个 LINQ 表达式上调用多个终端操作(Count、ToList、First、Any)时,会出现错误:



var query = numbers.Where(n => n % 2 == 0);int count1 = query.Count(); // 第一次遍历int count2 = query.Count(); // 第二次遍历!// 集合被枚举了两次。


每次调用 Count() 或 ToList() 都会重新执行过滤。如果过滤条件复杂,或者数据源是数据库(Entity Framework),这会导致双重查询。



正确做法:物化



如果您需要多次使用查询结果,请将其保存到列表或数组中:



var query = numbers.Where(n => n % 2 == 0).ToList(); // 一次遍历int count1 = query.Count;int count2 = query.Count;


提示: 如果您计划调用多个方法(Count、First、Sum),请在过滤后立即使用 .ToList() 或 .ToArray()。对于单次调用,则保持原样。

博客

书籍推荐