Enumerable 下又有新的扩展方法啦,快来一睹为快吧
一:背景
1. 講故事
前段時(shí)間將公司的一個(gè)項(xiàng)目從 4.5 升級(jí)到了 framework 4.8 ,編碼的時(shí)候發(fā)現(xiàn) Enumerable 中多了三個(gè)擴(kuò)展方法:?Append, Prepend, ToHashSet,想必玩過(guò)jquery的朋友一眼就能看出這三個(gè)方法的用途,這篇就和大家一起來(lái)聊聊這三個(gè)方法的底層源碼實(shí)現(xiàn),看有沒(méi)有什么新東西可以挖出來(lái)。
二:Enumerable 下的新擴(kuò)展方法
1. Append
看到這個(gè)我的第一印象就是?Add?方法, 可惜在 Enumerable 中并沒(méi)有類似的方法,可能后來(lái)程序員在這塊的呼聲越來(lái)越高,C#開(kāi)發(fā)團(tuán)隊(duì)就彌補(bǔ)了這個(gè)遺憾。
<1> 單條數(shù)據(jù)的追加
接下來(lái)我寫一個(gè)小例子往集合的尾部追加一條數(shù)據(jù),如下代碼所示:
static void Main(string[] args){var arr = new int[2] { 1, 2 };var result = Enumerable.Append(arr, 3);foreach (var item in result){Console.WriteLine(item);}}邏輯還是非常清晰的,再來(lái)看看底層源碼是怎么實(shí)現(xiàn)的。
public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element) {if (source == null){throw Error.ArgumentNull("source");}AppendPrependIterator<TSource> appendPrependIterator = source as AppendPrependIterator<TSource>;if (appendPrependIterator != null){return appendPrependIterator.Append(element);}return new AppendPrepend1Iterator<TSource>(source, element, appending: true); }private class AppendPrepend1Iterator<TSource> : AppendPrependIterator<TSource> {public AppendPrepend1Iterator(IEnumerable<TSource> source, TSource item, bool appending) : base(source){_item = item;_appending = appending;}public override bool MoveNext(){switch (state){case 1:state = 2;if (!_appending){current = _item;return true;}goto case 2;case 2:GetSourceEnumerator();state = 3;goto case 3;case 3:if (LoadFromEnumerator()){return true;}if (_appending){current = _item;return true;}break;}Dispose();return false;}}從上面的源碼來(lái)看,這玩意做的還是挺復(fù)雜的,繼承關(guān)系依次是:?AppendPrepend1Iterator<TSource> -> AppendPrependIterator<TSource> -> Iterator<TSource>, 這里大家要著重看一下?MoveNext()?里面的兩個(gè)方法 GetSourceEnumerator() 和 LoadFromEnumerator(),如下代碼所示:
可以看到,第一個(gè)方法用于獲取 Array 這個(gè)數(shù)據(jù)源,下面這個(gè)方法用于遍歷這個(gè) Array,當(dāng) foreach 遍歷完之后,執(zhí)行 case 3 語(yǔ)句,也就是下面的 if 語(yǔ)句,將你追加的 3 迭代一下,如下圖:
<2> 批量數(shù)據(jù)的追加
我們知道集合的添加除了 Add 還有 AddRange,很遺憾,Enumerable下并沒(méi)有找到類似的 AppendRange 方法,那如果要實(shí)現(xiàn) AppendRange 操作該怎么處理呢?哈哈,只能自己 foreach 迭代啦,如下代碼:
static void Main(string[] args){var arr = new int[2] { 1, 2 };var arr2 = new int[3] { 3, 4, 5 };IEnumerable<int> collection = arr;foreach (var item in arr2){collection = collection.Append(item);}foreach (var item in collection){Console.WriteLine(item);}}結(jié)果也是非常簡(jiǎn)單的,因?yàn)?IEnumerable 是非破壞性的操作,所以你需要在 Append 之后用類型給接住,接下來(lái)找一下底層源碼。
public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element) {if (source == null){throw Error.ArgumentNull("source");}AppendPrependIterator<TSource> appendPrependIterator = source as AppendPrependIterator<TSource>;if (appendPrependIterator != null){return appendPrependIterator.Append(element);}return new AppendPrepend1Iterator<TSource>(source, element, appending: true); }private class AppendPrepend1Iterator<TSource> : AppendPrependIterator<TSource> {public override AppendPrependIterator<TSource> Append(TSource item){if (_appending){return new AppendPrependN<TSource>(_source, null, new SingleLinkedNode<TSource>(_item).Add(item), 0, 2);}return new AppendPrependN<TSource>(_source, new SingleLinkedNode<TSource>(_item), new SingleLinkedNode<TSource>(item), 1, 1);} }private class AppendPrependN<TSource> : AppendPrependIterator<TSource> {public override AppendPrependIterator<TSource> Append(TSource item){SingleLinkedNode<TSource> appended = (_appended != null) ? _appended.Add(item) : new SingleLinkedNode<TSource>(item);return new AppendPrependN<TSource>(_source, _prepended, appended, _prependCount, _appendCount + 1);} }從上面的代碼可以看出,當(dāng)你 Append 多次的時(shí)候,本質(zhì)上就是多次調(diào)用?AppendPrependN<TSource>.Append()?,而且在調(diào)用的過(guò)程中,一直將你后續(xù)添加的元素追加到?SingleLinkedNode?單鏈表中,這里要注意的是 Add 采用的是 頭插法,所以最后插入的元素會(huì)在隊(duì)列頭部,如下圖:
如果你不信的話,我可以在 vs 調(diào)試中給您展示出來(lái)。
貌似說(shuō)的有點(diǎn)啰嗦,最后大家觀察一下?AppendPrependN<TSource>.MoveNext?的實(shí)現(xiàn)就可以了。
說(shuō)了這么多,我想你應(yīng)該明白了哈。
2. Prepend
本質(zhì)上來(lái)說(shuō) Prepend 和 Append 是一對(duì)的,一個(gè)是在前面插入,一個(gè)是在后面插入,不要想歪了,如果你細(xì)心的話,你會(huì)發(fā)現(xiàn) Prepend 也是用了這三個(gè)類:?AppendPrepend1Iterator<TSource>,AppendPrependIterator<TSource>,AppendPrependN<TSource>?以及 單鏈表?SingleLinkedNode<TSource>,這個(gè)就留給大家自己研究了哈。
3. ToHashSet
我以前在全內(nèi)存開(kāi)發(fā)中會(huì)頻繁的用到 HashSet,畢竟它的時(shí)間復(fù)雜度是?O(1)?,而且在 Enumerable 中早就有了 ToList 和 ToDictionary,憑啥沒(méi)有 ToHashSet,在以前只能將 source 塞到 HashSet 的構(gòu)造函數(shù)中,如:?new HashSet<int>(source)?,想想也是夠奇葩的哈,而且我還想吐糟一下的是居然到現(xiàn)在還沒(méi)有 AddRange 批量添加方法,氣人哈,接下來(lái)用 ILSpy 看一下這個(gè)擴(kuò)展方法是如何實(shí)現(xiàn)的。
三:總結(jié)
總體來(lái)說(shuō)這三個(gè)方法還是很實(shí)用的,我相信在后續(xù)的版本中 Enumerable 下的擴(kuò)展方法還會(huì)越來(lái)越多,越來(lái)越人性化,人生苦短, 我用C#。
總結(jié)
以上是生活随笔為你收集整理的Enumerable 下又有新的扩展方法啦,快来一睹为快吧的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 关于Dapper实现读写分离的个人思考
- 下一篇: 旧 WCF 项目迁移到 asp.net