[C#] LINQ中String的Aggregate的性能問題以及提升性能。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章