在C#里,字符串类型具有不可变性,同时由CLR通过驻留池统一管理。理解这一机制是避开日常处理陷阱的前提。很多看似简单的文本拼接、截取或编码转换,背后都牵涉内存分配与对象生命周期的问题。

一、字符串不可变性与拼接陷阱
string在C#中是被 sealed 修饰的引用类型,但其使用方式类似值类型。每次使用加号拼接,编译器实际会创建新的字符串对象,原对象若无引用将被回收。在循环体内反复拼接会带来大量临时对象,进而触发频繁GC。
下面这段代码在循环中直接拼接,处理上万次时性能极差:
using System;
class Program
{
static void Main()
{
string result = string.Empty;
for (int i = 0; i < 10000; i++)
{
result = result + i.ToString() + ",";
}
Console.WriteLine(result.Length);
}
}
改用StringBuilder可显著减少分配。它在内部维护可变的字符缓冲区,只在容量不足时扩容。对于已知大致长度的拼接,建议先指定容量。
using System;
using System.Text;
class Program
{
static void Main()
{
StringBuilder sb = new StringBuilder(20000);
for (int i = 0; i < 10000; i++)
{
sb.Append(i).Append(',');
}
string result = sb.ToString();
Console.WriteLine(result.Length);
}
}
二、编码错误导致的乱码问题
当字符串与字节数组互转时,若两端编码不一致就会产生乱码。常见错误是从网络或文件读取字节后,未显式指定编码就调用string构造器,此时会采用系统默认编码,在跨平台部署时极易出错。
稳妥做法是在所有IO边界明确使用UTF8。以下示例展示正确读取文本流的方式:
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
byte[] bytes = File.ReadAllBytes("test.txt");
string content = Encoding.UTF8.GetString(bytes);
Console.WriteLine(content);
}
}
若需将字符串写回,也应使用同一编码。很多日志组件出现中文乱码,根因就是写文件时用了默认ANSI而读端按UTF8解析。统一编码规范能从源头消灭这类问题。
三、Substring与Split的注意事项
Substring方法在越界时会抛出ArgumentOutOfRangeException,因此在截取前必须校验长度。某些旧版本.NET中Substring会复制原字符串部分内容,虽不影响正确性但需注意大对象堆分配。
Split方法若传入空字符或过多分隔符,可能产生大量空项。使用StringSplitOptions.RemoveEmptyEntries可过滤无效数据:
using System;
class Program
{
static void Main()
{
string data = "a,,b,c,";
string[] parts = data.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var p in parts)
{
Console.WriteLine(p);
}
}
}
在解析报文或CSV时,建议优先使用专门库而非手工Split,以避免引号内逗号被误切。手动处理仅适合格式极度可控的简单场景。
四、字符串格式化与插值选择
string.Format提供占位符语法,适合需要本地化或动态模板的场景。C# 6引入的$插值字符串在编译期转换为Format调用,可读性好且不易错位。
using System;
class Program
{
static void Main()
{
string name = "Tom";
int age = 20;
string msg = $"姓名:{name},年龄:{age}";
Console.WriteLine(msg);
}
}
但需注意,插值字符串在频繁拼接且带条件分支时,仍可能生成多此对象。高性能日志建议结合StringBuilder.AppendLine格式化输出,避免不必要的字符串分配。
五、驻留机制与Equals比较
CLR会对字面量字符串自动驻留,因此两个相同字面量用==比较返回true。但动态拼接出的字符串默认不驻留,若需复用可用string.Intern显式加入池,不过滥用会增大内存驻留表。
比较内容时推荐string.Equals并传入StringComparison规则,尤其处理大小写不敏感场景应指定OrdinalIgnoreCase,避免依赖当前区域设置引发隐蔽bug。
using System;
class Program
{
static void Main()
{
string a = "hello";
string b = "HELLO";
bool eq = a.Equals(b, StringComparison.OrdinalIgnoreCase);
Console.WriteLine(eq);
}
}
综上,掌握不可变本质、选对拼接工具、统一编码、谨慎截取分割并规范比较方式,就能规避C#字符串操作的主要坑点,写出稳定且高效的服务端代码。
C#string_operationencoding修改时间:2026-08-09 00:33:28