String作为不可变类(Immutable class),被创建之后便无法修改。所以一般有经验的程序员在处理String串联问题的时候都会使用StringBuffer和StringBuilder来提升String串联性能。
假设我们有很多错误消息(Error Message),我们想要把这些消息都串联起来。并显示。
List<string> messages = new List<string>() { "Message1", "Message2", "Message3" };
string connectedMessages = messages.Aggregate((str1, str2) => str1 + " | " + str2);
一般来说写法如上,Aggregate方法三个重载里的最常用的。
Aggregate<TSource>(IEnumerable<TSource>, Func<TSource,TSource,TSource>)
上述代码串联结果如下
"Message1 | Message2 | Message3"
这个方法可以比写For循环来串联的方便,但是也同时带来了一个问题,就是多创建冗余的字符串导致性能下降。这个性能下降在List比较小的时候不会特别明显,但是当数据量增大的时候会出现明显的性能下降,比如我们要自己组装字符串并设置给粘贴板(Clipboard)的时候。大量的字符串、‘\t‘和NewLine的拼接会导致性能急剧下降,这个时候我们就需要考虑到性能优化问题。
这个时候如果不仔细调查,一般会回到For循环+ StringBuilder / StringBuffer的拥抱,这样我们就抛弃了极易阅读、极易书写的LINQ的优点,这是我们不愿意看到的。所以我们希望能在LINQ的Aggregate方法里能够使用StringBuilder等来提高性能。
这个时候我们可以使用Aggregate方法的其他两个重载来实现。我们来看一下这两个方法的定义。
Aggregate<TSource,TAccumulate,TResult>(IEnumerable<TSource>, TAccumulate, Func<TAccumulate,TSource,TAccumulate>, Func<TAccumulate,TResult>)
Aggregate<TSource,TAccumulate>(IEnumerable<TSource>, TAccumulate, Func<TAccumulate,TSource,TAccumulate>)
第一眼看到这两个方法的定义的时候相信大家一定是心里骂“卧槽,这定义,什么鬼“。笔者最初也是一脸懵逼。不过经过调查之后发现能够使用以上的方法实现在Aggregate里使用StringBuilder的需求。那么我们来看一下如何实现的吧。
string connectedMessages = messages.Aggregate(new StringBuilder(),
(sb, str) =>
{
return sb.Append(str).Append(" | ");
},
sb =>
sb.Remove(sb.Length - 3, 3).ToString());//移除最后多余添加的“ | “
以上,便能实现在Aggregate里使用StringBuilder的需求了。
参考文献:
[1] Queryable.Aggregate Method, (2019/03/03) Retrieved https://docs.microsoft.com/en-us/dotnet/api/system.linq.queryable.aggregate?view=netframework-4.7.2