IEnumerable 的小例子有哪些
发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,今天就跟大家聊聊有关 IEnumerable 的小例子有哪些,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。全是源码每个以 TXX 开头命名的均
千家信息网最后更新 2025年02月01日IEnumerable 的小例子有哪些
今天就跟大家聊聊有关 IEnumerable 的小例子有哪些,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
全是源码
每个以 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}; // 通过本地方法合并两个数组为一个数据 var result1 = ConcatArray(array1, array2).ToArray(); // 使用 Linq 中的 Concat 来合并两个 IEnumerable 对象 var result2 = array1.Concat(array2).ToArray(); // 使用 Linq 中的 SelectMany 将 "二维数据" 拉平合并为一个数组 var result3 = new[] {array1, array2}.SelectMany(x => x).ToArray(); /** * 使用 Enumerable.Range 生成一个数组,这个数据的结果为 * 0,1,2,3,4,5,6,7,8,9 */ var result = Enumerable.Range(0, 10).ToArray(); // 通过以上三种方式合并的结果时相同的 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拉平三重循环() { /** * 通过本地函数获取 0-999 共 1000 个数字。 * 在 GetSomeData 通过三重循环构造这些数据 * 值得注意的是 GetSomeData 隐藏了三重循环的细节 */ var result1 = GetSomeData(10, 10, 10) .ToArray(); /** * 与 GetSomeData 方法对比,将"遍历"和"处理"两个逻辑进行了分离。 * "遍历"指的是三重循环本身。 * "处理"指的是三重循环最内部的加法过程。 * 这里通过 Select 方法,将"处理"过程抽离了出来。 * 这其实和 "T03分离条件"中使用 Where 使用的是相同的思想。 */ var result2 = GetSomeData2(10, 10, 10) .Select(tuple => tuple.i * 100 + tuple.j * 10 + tuple.k) .ToArray(); // 生成一个 0-999 的数组。 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 (); } /// /// 当前节点的值 /// public int Value { get; set; } ////// 当前节点的子节点列表 /// public IEnumerableChildren { get; set; } } [Fact] public void T13遍历树() { /** * 树结构如下: * └─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 }, } }; // 深度优先遍历的结果 var dftResult = new[] {0, 1, 3, 2}; // 通过迭代器实现深度优先遍历 var dft = DFTByEnumerable(tree).ToArray(); dft.Should().Equal(dftResult); // 使用堆栈配合循环算法实现深度优先遍历 var dftList = DFTByStack(tree).ToArray(); dftList.Should().Equal(dftResult); // 递归算法实现深度优先遍历 var dftByRecursion = DFTByRecursion(tree).ToArray(); dftByRecursion.Should().Equal(dftResult); // 广度优先遍历的结果 var bdfResult = new[] {0, 1, 2, 3}; /** * 通过迭代器实现广度优先遍历 * 此处未提供"通过队列配合循环算法"和"递归算法"实现广度优先遍历的两种算法进行对比。读者可以自行尝试。 */ var bft = BFT(tree).ToArray(); bft.Should().Equal(bdfResult); /** * 迭代器深度优先遍历 * 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; } } } // 使用堆栈配合循环算法实现深度优先遍历 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; } // 递归算法实现深度优先遍历 IEnumerable DFTByRecursion(TreeNode root) { var list = new List {root.Value}; foreach (var rootChild in root.Children) { list.AddRange(DFTByRecursion(rootChild)); } return list; } // 通过迭代器实现广度优先遍历 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搜索树() { /** * 此处所指的搜索树是指在遍历树的基础上增加终结遍历的条件。 * 因为一般构建搜索树是为了找到第一个满足条件的数据,因此与单纯的遍历存在不同。 * 树结构如下: * └─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 }, } }, } }; /** * 有了深度优先遍历算法的情况下,再增加一个条件判断,便可以实现深度优先的搜索 * 搜索树中第一个大于等于 3 并且是奇数的数字 */ var result = DFS(tree, x => x >= 3 && x % 2 == 1); /** * 搜索到的结果是3。 * 特别提出,如果使用广度优先搜索,结果应该是5。 * 读者可以通过 T13遍历树 中的广度优先遍历算法配合 FirstOrDefault 中相同的条件实现。 * 建议读者尝试以上代码尝试一下。 */ result.Should().Be(3); int DFS(TreeNode root, Func predicate) { var re = DFTByEnumerable(root) .FirstOrDefault(predicate); return re; } // 迭代器深度优先遍历 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}; // 使用迭代器进行分页,每 3 个一页 var enumerablePagedResult = PageByEnumerable(arraySource, 3).ToArray(); // 结果一共 4 页 enumerablePagedResult.Should().HaveCount(4); // 最后一页只有一个数字,为 9 enumerablePagedResult.Last().Should().Equal(9); // 通过常规的 Skip 和 Take 来分页是最为常见的办法。结果应该与上面的分页结果一样 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 (); } // 最后一页如果数据不足一页,也应该返回该页 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 写法在仅仅只有在一些特殊的情况下才能体现性能上的优势,可读性上却不如 NormalPage */ } [Fact] public void T16分页与多级缓存() { /** * 获取 5 页数据,每页 2 个。 * 依次从 内存、Redis、ElasticSearch和数据库中获取数据。 * 先从内存中获取数据,如果内存中数据不足页,则从 Redis 中获取。 * 若 Redis 获取后还是不足页,进而从 ElasticSearch 中获取。依次类推,直到足页或者再无数据 */ const int pageSize = 2; const int pageCount = 5; var emptyData = Enumerable.Empty ().ToArray(); /** * 初始化各数据源的数据,除了内存有数据外,其他数据源均没有数据 */ var memoryData = new[] {0, 1, 2}; var redisData = emptyData; var elasticSearchData = emptyData; var databaseData = emptyData; var result = GetSourceData() // ToPagination 是一个扩展方法。此处是为了体现链式调用的可读性,转而使用扩展方法,没有使用本地函数 .ToPagination(pageCount, pageSize) .ToArray(); result.Should().HaveCount(2); result[0].Should().Equal(0, 1); result[1].Should().Equal(2); /** * 初始化各数据源数据,各个数据源均有一些数据 */ 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() { // 将多数据源的数据连接在一起 var data = GetDataSource() .SelectMany(x => x); return data; // 获取数据源 IEnumerable > GetDataSource() { // 将数据源依次返回 yield return GetFromMemory(); yield return GetFromRedis(); yield return GetFromElasticSearch(); yield return GetFromDatabase(); } IEnumerable GetFromMemory() { _testOutputHelper.WriteLine("正在从内存中获取数据"); return memoryData; } IEnumerable GetFromRedis() { _testOutputHelper.WriteLine("正在从Redis中获取数据"); return redisData; } IEnumerable GetFromElasticSearch() { _testOutputHelper.WriteLine("正在从ElasticSearch中获取数据"); return elasticSearchData; } IEnumerable GetFromDatabase() { _testOutputHelper.WriteLine("正在从数据库中获取数据"); return databaseData; } } /** * 值得注意的是: * 由于 Enumerable 按需迭代的特性,如果将 result2 的所属页数改为只获取 1 页。 * 则在执行数据获取时,将不会再控制台中输出从 Redis、ElasticSearch和数据库中获取数据。 * 也就是说,并没有执行这些操作。读者可以自行修改以上代码,加深印象。 */ } } public static class EnumerableExtensions { /// /// 将原数据分页 /// /// 数据源 /// 页数 /// 页大小 ///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++; // 如果获取的数量已经达到了分页所需要的总数,则停止进一步迭代 if (countNow == maxCount) { break; } if (onePage.Count != pageSize) { continue; } yield return onePage; onePage = new LinkedList (); } // 最后一页如果数据不足一页,也应该返回该页 if (onePage.Count > 0) { yield return onePage; } } }}
看完上述内容,你们对 IEnumerable 的小例子有哪些有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。
数据
深度
算法
结果
数据源
循环
迭代
搜索
广度
内存
方法
条件
数组
正在
读者
相同
两个
内容
写法
数字
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
云栖大会阿里云数据库
吉林潮流软件开发服务应用
石家庄市软件开发
2021年度网络安全教育微课
汕头考试软件开发电话
招商科技 软件开发
软件开发年终总结报告
工控网络安全的规范
mt4服务器端
计算机软件开发公司税率
网络安全法诈骗宣传
数据库表导入却是空的mysql
假定卫星信道的数据库
瑞挺网络技术
简述你身边的网络安全威胁实例
荣成软件开发自学网哪里好
数据库运维入门
第五人格一直在服务器是咋回事
湖北ipfs云服务器云空间
软件开发常用技术要求
如何连接数据库服务器2008
数据库更新语句不成功
金融机构网络安全周宣传
传达网络安全工作会议精神
江阴大型软件开发项目信息
昆明游戏app软件开发
数据库转数据仓库
全球3d街景软件开发公司电话
职高计算机网络技术课程
密集网络技术的应用