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