今天就跟大家聊聊有關(guān) IEnumerable 的小例子有哪些,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
成都創(chuàng)新互聯(lián)是專業(yè)的東昌府網(wǎng)站建設(shè)公司,東昌府接單;提供成都網(wǎng)站建設(shè)、做網(wǎng)站,網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行東昌府網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
每個以 TXX 開頭命名的均是一個示例。建議從上往下閱讀。
using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; using Xunit; using Xunit.Abstractions; namespace Try_More_On_IEnumerable { public class EnumerableTests2 { private readonly ITestOutputHelper _testOutputHelper; public EnumerableTests2( ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; } [Fact] public void T11分組合并() { var array1 = new[] {0, 1, 2, 3, 4}; var array2 = new[] {5, 6, 7, 8, 9}; // 通過本地方法合并兩個數(shù)組為一個數(shù)據(jù) var result1 = ConcatArray(array1, array2).ToArray(); // 使用 Linq 中的 Concat 來合并兩個 IEnumerable 對象 var result2 = array1.Concat(array2).ToArray(); // 使用 Linq 中的 SelectMany 將 “二維數(shù)據(jù)” 拉平合并為一個數(shù)組 var result3 = new[] {array1, array2}.SelectMany(x => x).ToArray(); /** * 使用 Enumerable.Range 生成一個數(shù)組,這個數(shù)據(jù)的結(jié)果為 * 0,1,2,3,4,5,6,7,8,9 */ var result = Enumerable.Range(0, 10).ToArray(); // 通過以上三種方式合并的結(jié)果時相同的 result1.Should().Equal(result); result2.Should().Equal(result); result3.Should().Equal(result); IEnumerableConcatArray (IEnumerable source1, IEnumerable source2) { foreach (var item in source1) { yield return item; } foreach (var item in source2) { yield return item; } } } [Fact] public void T12拉平三重循環(huán)() { /** * 通過本地函數(shù)獲取 0-999 共 1000 個數(shù)字。 * 在 GetSomeData 通過三重循環(huán)構(gòu)造這些數(shù)據(jù) * 值得注意的是 GetSomeData 隱藏了三重循環(huán)的細(xì)節(jié) */ var result1 = GetSomeData(10, 10, 10) .ToArray(); /** * 與 GetSomeData 方法對比,將“遍歷”和“處理”兩個邏輯進(jìn)行了分離。 * “遍歷”指的是三重循環(huán)本身。 * “處理”指的是三重循環(huán)最內(nèi)部的加法過程。 * 這里通過 Select 方法,將“處理”過程抽離了出來。 * 這其實(shí)和 “T03分離條件”中使用 Where 使用的是相同的思想。 */ var result2 = GetSomeData2(10, 10, 10) .Select(tuple => tuple.i * 100 + tuple.j * 10 + tuple.k) .ToArray(); // 生成一個 0-999 的數(shù)組。 var result = Enumerable.Range(0, 1000).ToArray(); result1.Should().Equal(result); result2.Should().Equal(result); IEnumerable GetSomeData(int maxI, int maxJ, int maxK) { for (var i = 0; i < maxI; i++) { for (var j = 0; j < maxJ; j++) { for (var k = 0; k < maxK; k++) { yield return i * 100 + j * 10 + k; } } } } IEnumerable<(int i, int j, int k)> GetSomeData2(int maxI, int maxJ, int maxK) { for (var i = 0; i < maxI; i++) { for (var j = 0; j < maxJ; j++) { for (var k = 0; k < maxK; k++) { yield return (i, j, k); } } } } } private class TreeNode { public TreeNode() { Children = Enumerable.Empty (); } /// /// 當(dāng)前節(jié)點(diǎn)的值 /// public int Value { get; set; } ////// 當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)列表 /// public IEnumerableChildren { get; set; } } [Fact] public void T13遍歷樹() { /** * 樹結(jié)構(gòu)如下: * └─0 * ├─1 * │ └─3 * └─2 */ var tree = new TreeNode { Value = 0, Children = new[] { new TreeNode { Value = 1, Children = new[] { new TreeNode { Value = 3 }, } }, new TreeNode { Value = 2 }, } }; // 深度優(yōu)先遍歷的結(jié)果 var dftResult = new[] {0, 1, 3, 2}; // 通過迭代器實(shí)現(xiàn)深度優(yōu)先遍歷 var dft = DFTByEnumerable(tree).ToArray(); dft.Should().Equal(dftResult); // 使用堆棧配合循環(huán)算法實(shí)現(xiàn)深度優(yōu)先遍歷 var dftList = DFTByStack(tree).ToArray(); dftList.Should().Equal(dftResult); // 遞歸算法實(shí)現(xiàn)深度優(yōu)先遍歷 var dftByRecursion = DFTByRecursion(tree).ToArray(); dftByRecursion.Should().Equal(dftResult); // 廣度優(yōu)先遍歷的結(jié)果 var bdfResult = new[] {0, 1, 2, 3}; /** * 通過迭代器實(shí)現(xiàn)廣度優(yōu)先遍歷 * 此處未提供“通過隊(duì)列配合循環(huán)算法”和“遞歸算法”實(shí)現(xiàn)廣度優(yōu)先遍歷的兩種算法進(jìn)行對比。讀者可以自行嘗試。 */ var bft = BFT(tree).ToArray(); bft.Should().Equal(bdfResult); /** * 迭代器深度優(yōu)先遍歷 * depth-first traversal */ IEnumerable DFTByEnumerable(TreeNode root) { yield return root.Value; foreach (var child in root.Children) { foreach (var item in DFTByEnumerable(child)) { yield return item; } } } // 使用堆棧配合循環(huán)算法實(shí)現(xiàn)深度優(yōu)先遍歷 IEnumerable DFTByStack(TreeNode root) { var result = new List (); var stack = new Stack (); stack.Push(root); while (stack.TryPop(out var node)) { result.Add(node.Value); foreach (var nodeChild in node.Children.Reverse()) { stack.Push(nodeChild); } } return result; } // 遞歸算法實(shí)現(xiàn)深度優(yōu)先遍歷 IEnumerable DFTByRecursion(TreeNode root) { var list = new List {root.Value}; foreach (var rootChild in root.Children) { list.AddRange(DFTByRecursion(rootChild)); } return list; } // 通過迭代器實(shí)現(xiàn)廣度優(yōu)先遍歷 IEnumerable BFT(TreeNode root) { yield return root.Value; foreach (var bftChild in BFTChildren(root.Children)) { yield return bftChild; } IEnumerable BFTChildren(IEnumerable children) { var tempList = new List (); foreach (var treeNode in children) { tempList.Add(treeNode); yield return treeNode.Value; } foreach (var bftChild in tempList.SelectMany(treeNode => BFTChildren(treeNode.Children))) { yield return bftChild; } } } } [Fact] public void T14搜索樹() { /** * 此處所指的搜索樹是指在遍歷樹的基礎(chǔ)上增加終結(jié)遍歷的條件。 * 因?yàn)橐话銟?gòu)建搜索樹是為了找到第一個滿足條件的數(shù)據(jù),因此與單純的遍歷存在不同。 * 樹結(jié)構(gòu)如下: * └─0 * ├─1 * │ └─3 * └─5 * └─2 */ var tree = new TreeNode { Value = 0, Children = new[] { new TreeNode { Value = 1, Children = new[] { new TreeNode { Value = 3 }, } }, new TreeNode { Value = 5, Children = new[] { new TreeNode { Value = 2 }, } }, } }; /** * 有了深度優(yōu)先遍歷算法的情況下,再增加一個條件判斷,便可以實(shí)現(xiàn)深度優(yōu)先的搜索 * 搜索樹中第一個大于等于 3 并且是奇數(shù)的數(shù)字 */ var result = DFS(tree, x => x >= 3 && x % 2 == 1); /** * 搜索到的結(jié)果是3。 * 特別提出,如果使用廣度優(yōu)先搜索,結(jié)果應(yīng)該是5。 * 讀者可以通過 T13遍歷樹 中的廣度優(yōu)先遍歷算法配合 FirstOrDefault 中相同的條件實(shí)現(xiàn)。 * 建議讀者嘗試以上代碼嘗試一下。 */ result.Should().Be(3); int DFS(TreeNode root, Func predicate) { var re = DFTByEnumerable(root) .FirstOrDefault(predicate); return re; } // 迭代器深度優(yōu)先遍歷 IEnumerable DFTByEnumerable(TreeNode root) { yield return root.Value; foreach (var child in root.Children) { foreach (var item in DFTByEnumerable(child)) { yield return item; } } } } [Fact] public void T15分頁() { var arraySource = new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // 使用迭代器進(jìn)行分頁,每 3 個一頁 var enumerablePagedResult = PageByEnumerable(arraySource, 3).ToArray(); // 結(jié)果一共 4 頁 enumerablePagedResult.Should().HaveCount(4); // 最后一頁只有一個數(shù)字,為 9 enumerablePagedResult.Last().Should().Equal(9); // 通過常規(guī)的 Skip 和 Take 來分頁是最為常見的辦法。結(jié)果應(yīng)該與上面的分頁結(jié)果一樣 var result3 = NormalPage(arraySource, 3).ToArray(); result3.Should().HaveCount(4); result3.Last().Should().Equal(9); IEnumerable > PageByEnumerable(IEnumerable source, int pageSize) { var onePage = new LinkedList (); foreach (var i in source) { onePage.AddLast(i); if (onePage.Count != pageSize) { continue; } yield return onePage; onePage = new LinkedList (); } // 最后一頁如果數(shù)據(jù)不足一頁,也應(yīng)該返回該頁 if (onePage.Count > 0) { yield return onePage; } } IEnumerable > NormalPage(IReadOnlyCollection source, int pageSize) { var pageCount = Math.Ceiling(1.0 * source.Count / pageSize); for (var i = 0; i < pageCount; i++) { var offset = i * pageSize; var onePage = source .Skip(offset) .Take(pageSize); yield return onePage; } } /** * 從寫法邏輯上來看,顯然 NormalPage 的寫法更容易讓大眾接受 * PageByEnumerable 寫法在僅僅只有在一些特殊的情況下才能體現(xiàn)性能上的優(yōu)勢,可讀性上卻不如 NormalPage */ } [Fact] public void T16分頁與多級緩存() { /** * 獲取 5 頁數(shù)據(jù),每頁 2 個。 * 依次從 內(nèi)存、redis、ElasticSearch和數(shù)據(jù)庫中獲取數(shù)據(jù)。 * 先從內(nèi)存中獲取數(shù)據(jù),如果內(nèi)存中數(shù)據(jù)不足頁,則從 Redis 中獲取。 * 若 Redis 獲取后還是不足頁,進(jìn)而從 ElasticSearch 中獲取。依次類推,直到足頁或者再無數(shù)據(jù) */ const int pageSize = 2; const int pageCount = 5; var emptyData = Enumerable.Empty ().ToArray(); /** * 初始化各數(shù)據(jù)源的數(shù)據(jù),除了內(nèi)存有數(shù)據(jù)外,其他數(shù)據(jù)源均沒有數(shù)據(jù) */ var memoryData = new[] {0, 1, 2}; var redisData = emptyData; var elasticSearchData = emptyData; var databaseData = emptyData; var result = GetSourceData() // ToPagination 是一個擴(kuò)展方法。此處是為了體現(xiàn)鏈?zhǔn)秸{(diào)用的可讀性,轉(zhuǎn)而使用擴(kuò)展方法,沒有使用本地函數(shù) .ToPagination(pageCount, pageSize) .ToArray(); result.Should().HaveCount(2); result[0].Should().Equal(0, 1); result[1].Should().Equal(2); /** * 初始化各數(shù)據(jù)源數(shù)據(jù),各個數(shù)據(jù)源均有一些數(shù)據(jù) */ memoryData = new[] {0, 1, 2}; redisData = new[] {3, 4, 5}; elasticSearchData = new[] {6, 7, 8}; databaseData = Enumerable.Range(9, 100).ToArray(); var result2 = GetSourceData() .ToPagination(pageCount, pageSize) .ToArray(); result2.Should().HaveCount(5); result2[0].Should().Equal(0, 1); result2[1].Should().Equal(2, 3); result2[2].Should().Equal(4, 5); result2[3].Should().Equal(6, 7); result2[4].Should().Equal(8, 9); IEnumerable GetSourceData() { // 將多數(shù)據(jù)源的數(shù)據(jù)連接在一起 var data = GetDataSource() .SelectMany(x => x); return data; // 獲取數(shù)據(jù)源 IEnumerable > GetDataSource() { // 將數(shù)據(jù)源依次返回 yield return GetFromMemory(); yield return GetFromRedis(); yield return GetFromElasticSearch(); yield return GetFromDatabase(); } IEnumerable GetFromMemory() { _testOutputHelper.WriteLine("正在從內(nèi)存中獲取數(shù)據(jù)"); return memoryData; } IEnumerable GetFromRedis() { _testOutputHelper.WriteLine("正在從Redis中獲取數(shù)據(jù)"); return redisData; } IEnumerable GetFromElasticSearch() { _testOutputHelper.WriteLine("正在從ElasticSearch中獲取數(shù)據(jù)"); return elasticSearchData; } IEnumerable GetFromDatabase() { _testOutputHelper.WriteLine("正在從數(shù)據(jù)庫中獲取數(shù)據(jù)"); return databaseData; } } /** * 值得注意的是: * 由于 Enumerable 按需迭代的特性,如果將 result2 的所屬頁數(shù)改為只獲取 1 頁。 * 則在執(zhí)行數(shù)據(jù)獲取時,將不會再控制臺中輸出從 Redis、ElasticSearch和數(shù)據(jù)庫中獲取數(shù)據(jù)。 * 也就是說,并沒有執(zhí)行這些操作。讀者可以自行修改以上代碼,加深印象。 */ } } public static class EnumerableExtensions { /// /// 將原數(shù)據(jù)分頁 /// /// 數(shù)據(jù)源 /// 頁數(shù) /// 頁大小 ///public static IEnumerable > ToPagination(this IEnumerable source, int pageCount, int pageSize) { var maxCount = pageCount * pageSize; var countNow = 0; var onePage = new LinkedList (); foreach (var i in source) { onePage.AddLast(i); countNow++; // 如果獲取的數(shù)量已經(jīng)達(dá)到了分頁所需要的總數(shù),則停止進(jìn)一步迭代 if (countNow == maxCount) { break; } if (onePage.Count != pageSize) { continue; } yield return onePage; onePage = new LinkedList (); } // 最后一頁如果數(shù)據(jù)不足一頁,也應(yīng)該返回該頁 if (onePage.Count > 0) { yield return onePage; } } } }
看完上述內(nèi)容,你們對 IEnumerable 的小例子有哪些有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。