C#性能优化的实用技巧,涵盖内存管理、集合操作、异步编程、字符串处理等关键领域。这些技巧旨在帮助你编写更高效的代码,减少资源消耗并提升执行速度。
每个技巧都配有简洁的说明和示例,突出优化的核心思想。
1. 使用值类型替代引用类型(当适用)技巧:
优先使用值类型(如 struct)而非引用类型(如 class),以减少堆分配和垃圾回收开销,适用于小型、短生命周期对象。示例:csharp
// 低效:使用 class 导致堆分配
public class PointClass
{
public int X { get; set; }
public int Y { get; set; }
}
// 高效:使用 struct 避免堆分配
public struct PointStruct
{
public int X { get; set; }
public int Y { get; set; }
}
注意:
- struct 适合简单数据结构(通常小于16字节)。
- 避免在大型结构或需要复杂继承的场景中使用 struct。
2. 使用 Span<T> 和 Memory<T> 优化内存访问技巧:使用 Span<T> 和 Memory<T> 进行高效的内存操作,减少复制和分配,尤其在处理数组、字符串或缓冲区时。示例:csharp
// 低效:多次分配新数组
public byte[] Extract(byte[] data, int start, int length)
{
byte[] result = new byte[length];
Array.Copy(data, start, result, 0, length);
return result;
}
// 高效:使用 Span<T> 避免分配
public ReadOnlySpan<byte> Extract(ReadOnlySpan<byte> data, int start, int length)
{
return data.Slice(start, length);
}
优势:
- Span<T> 提供零拷贝的内存切片操作。
- 适用于高性能场景,如解析数据或处理流。
3. 优化字符串操作技巧:使用 StringBuilder 或字符串插值代替频繁的字符串拼接,减少内存分配。示例:csharp
// 低效:字符串拼接导致多次分配
public string BuildMessage(string[] items)
{
string result = "";
foreach (var item in items)
{
result += item + ",";
}
return result;
}
// 高效:使用 StringBuilder
public string BuildMessage(string[] items)
{
StringBuilder sb = new StringBuilder();
foreach (var item in items)
{
sb.Append(item).Append(",");
}
return sb.ToString();
}
额外技巧:
- 使用 string.Create(C# 8.0+)创建字符串,避免中间对象:
csharp
public string CreateFormattedString(int value)
{
return string.Create(10, value, (span, v) => v.ToString().AsSpan().CopyTo(span));
}
4. 选择合适的集合类型技巧:根据使用场景选择合适的集合类型(如 List<T>、Dictionary<TKey, TValue>、HashSet<T>),避免不必要的查找或分配开销。示例:csharp
// 低效:List<T> 用于频繁查找
public bool ContainsValue(List<int> numbers, int value)
{
return numbers.Contains(value); // O(n) 查找
}
// 高效:使用 HashSet<T>
public bool ContainsValue(HashSet<int> numbers, int value)
{
return numbers.Contains(value); // O(1) 查找
}
选择指南:
- 频繁查找:HashSet<T> 或 Dictionary<TKey, TValue>(O(1))。
- 顺序访问/追加:List<T>(O(1) 追加)。
- 固定大小:Array(最少开销)。
- 线程安全:ConcurrentDictionary<TKey, TValue> 等并发集合。
5. 异步编程优化技巧:使用 ValueTask 代替 Task 在高频调用场景中减少分配,结合 async/await 避免阻塞。示例:csharp
// 低效:每次调用分配 Task
public async Task<int> GetCachedValueAsync()
{
if (_cache.TryGetValue(key, out var value))
{
return value;
}
return await FetchValueAsync();
}
// 高效:使用 ValueTask
public async ValueTask<int> GetCachedValueAsync()
{
if (_cache.TryGetValue(key, out var value))
{
return value; // 无分配
}
return await FetchValueAsync();
}
额外技巧:
- 避免 Task.Run 在高并发场景中过度使用,优先使用 I/O-bound 的异步 API。
- 使用 ConfigureAwait(false) 在不需要同步上下文的场景中:
csharp
public async Task ProcessAsync()
{
await Task.Delay(1000).ConfigureAwait(false); // 减少上下文切换开销
}
6. 缓存频繁访问的数据技巧:缓存计算结果或频繁访问的数据,减少重复计算或 I/O 操作。示例:csharp
// 低效:重复计算
public decimal CalculateTotal(IEnumerable<decimal> items)
{
return items.Sum(); // 每次调用都重新计算
}
// 高效:缓存结果
private decimal? _totalCache;
public decimal CalculateTotal(IEnumerable<decimal> items)
{
if (!_totalCache.HasValue)
{
_totalCache = items.Sum();
}
return _totalCache.Value;
}
注意:
- 在数据更改时重置缓存(如在 set 方法中)。
- 使用 Lazy<T> 或 MemoryCache 实现更复杂的缓存逻辑。
7. 避免不必要的装箱技巧:使用泛型和值类型接口(如 IEquatable<T>)避免装箱,减少 GC 压力。示例:csharp
// 低效:装箱发生
public bool Contains(object value, ArrayList list)
{
return list.Contains(value); // 装箱 int 到 object
}
// 高效:使用泛型
public bool Contains<T>(T value, List<T> list)
{
return list.Contains(value); // 无装箱
}
额外技巧:
- 为自定义值类型实现 IEquatable<T>,优化比较:
csharp
public struct Point : IEquatable<Point>
{
public int X, Y;
public bool Equals(Point other) => X == other.X && Y == other.Y;
}
8. 优化循环和迭代技巧:减少循环中的冗余操作,使用高效的迭代方式(如 for 代替 foreach 在性能敏感场景)。示例:csharp
// 低效:foreach 可能引入额外开销
public int SumArray(int[] numbers)
{
int sum = 0;
foreach (var n in numbers)
{
sum += n;
}
return sum;
}
// 高效:使用 for 循环
public int SumArray(int[] numbers)
{
int sum = 0;
for (int i = 0; i < numbers.Length; i++)
{
sum += numbers[i];
}
return sum;
}
额外技巧:
- 使用 Array.Length 缓存循环边界,避免重复访问。
- 对于多维数组,优化访问顺序以利用 CPU 缓存(如按行遍历)。
9. 使用池化技术减少对象分配技巧:使用对象池(如 ArrayPool<T> 或自定义池)减少频繁的对象分配和回收。示例:csharp
// 低效:频繁分配数组
public byte[] ProcessData(int size)
{
byte[] buffer = new byte[size];
// 处理逻辑
return buffer;
}
// 高效:使用 ArrayPool
public byte[] ProcessData(int size)
{
var pool = ArrayPool<byte>.Shared;
byte[] buffer = pool.Rent(size);
try
{
// 处理逻辑
return buffer;
}
finally
{
pool.Return(buffer);
}
}
优势:
- 减少 GC 压力,提高性能。
- 适合高频分配的场景(如 Web 服务器处理请求)。
10. 避免 LINQ 在性能敏感场景技巧:在高性能场景中,避免 LINQ 的链式调用,使用显式循环以减少迭代器开销。示例:csharp
// 低效:LINQ 多次遍历
public List<int> FilterAndDouble(IEnumerable<int> numbers)
{
return numbers.Where(n => n % 2 == 0).Select(n => n * 2).ToList();
}
// 高效:单次遍历
public List<int> FilterAndDouble(IEnumerable<int> numbers)
{
var result = new List<int>();
foreach (var n in numbers)
{
if (n % 2 == 0)
{
result.Add(n * 2);
}
}
return result;
}
注意:
- LINQ 适合快速开发和可读性,但在性能敏感场景(如实时系统)优先使用显式循环。
总结以下是性能优化技巧的总结表:
|
优化领域 |
技巧 |
优势 |
|---|---|---|
|
值类型 vs 引用类型 |
使用 struct 代替 class |
减少堆分配和 GC 开销 |
|
内存访问 |
使用 Span<T> 和 Memory<T> |
零拷贝,高效内存操作 |
|
字符串操作 |
使用 StringBuilder 或字符串插值 |
减少内存分配 |
|
集合选择 |
选择合适的集合类型 |
优化查找、插入等操作 |
|
异步编程 |
使用 ValueTask 和 ConfigureAwait |
减少分配和上下文切换 |
|
数据缓存 |
缓存计算结果 |
避免重复计算 |
|
装箱优化 |
使用泛型和值类型接口 |
减少 GC 压力 |
|
循环优化 |
使用 for 循环,缓存边界 |
减少迭代器开销 |
|
对象池 |
使用 ArrayPool<T> 或自定义池 |
减少对象分配和回收 |
|
LINQ 优化 |
避免 LINQ 链式调用,使用显式循环 |
减少迭代器开销 |
额外建议:
- 使用性能分析工具(如 BenchmarkDotNet、dotTrace)测量优化效果。
- 结合业务场景权衡可读性和性能(如 LINQ 在非性能敏感场景仍可使用)。
- 关注 .NET 版本特性(如 Span<T> 在 .NET Core 2.1+ 引入)。
如果需要针对特定场景的优化建议或更详细的代码示例,请告诉我!
1132

被折叠的 条评论
为什么被折叠?



