一、C# 拼接字符串的几种方式和性能
- 对于少量固定的字符串拼接,如string s= "a" + "b" + "c",系统会优化成s= String.Concat("a","b","c"),不会新建多个字符串。然而如果写成string s="a"; s +="b"; s+="c";则会创建三个新的字符串。
- string s= String.Format("{0}{1}{2}","a","b","c"); 看一下源代码:
public static String Format( IFormatProvider provider, String format, params Object[] args)
{
if (format == null || args == null)
throw new ArgumentNullException((format==null)?"format":"args");
StringBuilder sb = new StringBuilder(format.Length + args.Length * 8);
sb.AppendFormat(provider,format,args);
return sb.ToString();
}
其内部封装了一个 StringBuilder, 效率比用 + 号要好;此方法非常适用于从少量组件字符串生成字符串的情况。
- StringBuilder 是预先创建一个比较大的内存,以后每次操作时判断容量上限是否不够用,如果不够用就自动把容量扩大一倍,修改的还是原来那个对象;字符串较少时,可以使用String.Format()代替;有些情况下,可能要将字符串合并在循环中,此时不知道要合并的源字符串的数量,而且源字符串的实际数量可能非常大。 StringBuilder 类专门用于此类方案
二、循环性能测试
- 编写如下代码:
public static string ConcactTest()
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
long start = GC.GetTotalMemory(true);
// 在这里写需要被测试内存消耗的代码
string ret = "";
for (int i = 0; i < 100000; i++)
{
ret += i;
}
watch.Stop();
var useTime = (double)watch.ElapsedMilliseconds / 1000;
GC.Collect();
// 确保所有内存都被GC回收
GC.WaitForFullGCComplete();
long end = GC.GetTotalMemory(true);
long useMemory = end - start;
Console.WriteLine($"ConcactTest: memory used[{useMemory}], time used[{useTime}]");
return ret;
}
public static string FormatTest()
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
long start = GC.GetTotalMemory(true);
// 在这里写需要被测试内存消耗的代码
string ret = "";
for (int i = 0; i < 100000; i++)
{
ret = string.Format("{0}{1}", ret, i);
}
watch.Stop();
var useTime = (double)watch.ElapsedMilliseconds / 1000;
GC.Collect();
// 确保所有内存都被GC回收
GC.WaitForFullGCComplete();
long end = GC.GetTotalMemory(true);
long useMemory = end - start;
Console.WriteLine($"FormatTest: memory used[{useMemory}], time used[{useTime}]");
return ret;
}
public static string StringBuilderTest()
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
long start = GC.GetTotalMemory(true);
// 在这里写需要被测试内存消耗的代码,例如
StringBuilder ret = new StringBuilder(10000);
for (int i = 0; i < 100000; i++)
{
ret.Append(i);
}
watch.Stop();
var useTime = (double)watch.ElapsedMilliseconds / 1000;
GC.Collect();
// 确保所有内存都被GC回收
GC.WaitForFullGCComplete();
long end = GC.GetTotalMemory(true);
long useMemory = end - start;
Console.WriteLine($"StringBuilderTest: memory used[{useMemory}], time used[{useTime}]");
return ret.ToString();
}
- 调用查看运行结果如下
ConcactTest: memory used[1960604], time used[17.636]
FormatTest: memory used[977616], time used[39.187]
StringBuilderTest: memory used[982440], time used[0.014]
可见内存时间占用 StringBuilder 更合适