[.NET] : LINQ Deferred Execution
前言 :
看到91跟小朱都分享了,延迟执行的文章。
- 91 : [.NET]延迟执行(Deferred Execution) 简单概念实作
- 小朱 :[.NET] LINQ 的延迟执行 (Deferred Execution)
唤醒了许久之前的记忆,记得也有对LINQ的运作下了一番功夫。
趁记忆还没有消失。简单的做个记录,也希望对有需要的开发人员有帮助。
说明 :
简单的说,在 Linq的延迟执行运作,主要有三个要点。
1. IEnumerable跟 foreach是 LINQ运作的核心。
2. IEnumerable套用 Decorator模式,对IEnumerable加入功能。
3. 使用 IEnumerable的扩充方法生成套用 Decorator的 IEnumerable,方便串接程序。
1. IEnumerable跟 foreach是 LINQ运作的核心。
在LINQ里是以IEnumerable做为运作的目标跟结果,并且以foreach来做结果列举的动作。
了解IEnumerable跟foreach之间的运作流程,是理解LINQ运作很重要的一步。
下面这段Code展示了拆解 Foreach机制后的程序代码,用来说明使用foreach列举IEnumerable时的程序流程。
(更细节的数据,可以参考 Design Patterns里 Iterator模式。)
原始码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public static void Test001() { // 建立字符串数组,并且转型为IEnumerable string [] stringArray = new string [] { "A" , "B" , "C" }; IEnumerable< string > stringEnumerable = stringArray; // 列举 IEnumerable使用 Foreach foreach ( string item in stringEnumerable) { Console.WriteLine(item); } // 拆解 Foreach得到的程序 IEnumerator< string > stringEnumerator = stringEnumerable.GetEnumerator(); while (stringEnumerator.MoveNext() == true ) { string item = stringEnumerator.Current; Console.WriteLine(item); } } |
2. IEnumerable套用 Decorator模式,对IEnumerable加入功能。
Decorator模式的主要功能是 :「将额外权责动态附加于对象身上,不必延生子类别及可弹性扩增功能。」
将 Decorator模式套用到 IEnumerable之后,
可以将功能附加到 IEnumerable接口,却又不改变使用foreach列举的用法。
下面这段Code实做一个过滤字符串用的Decorator,用来说明如何将 Decorator模式套用到 IEnumerable
(更细节的数据,可以参考 Design Patterns里 Decorator模式。)
原始码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class StringFilterEnumerable : IEnumerable< string > { // Fields private readonly IEnumerable< string > _sourceEnumerable; private readonly string _filterArgument = null ; // Constructor public StringFilterEnumerable(IEnumerable< string > sourceEnumerable, string filterArgument) { #region Require if (sourceEnumerable == null ) throw new ArgumentNullException(); if ( string .IsNullOrEmpty(filterArgument) == true ) throw new ArgumentNullException(); #endregion _sourceEnumerable = sourceEnumerable; _filterArgument = filterArgument; } // Methods public IEnumerator< string > GetEnumerator() { return new StringFilterEnumerator(_sourceEnumerable.GetEnumerator(), _filterArgument); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this .GetEnumerator(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | public class StringFilterEnumerator : IEnumerator< string > { // Fields private readonly IEnumerator< string > _sourceEnumerator; private readonly string _filterArgument = null ; private string _currentResult = null ; // Constructor public StringFilterEnumerator(IEnumerator< string > sourceEnumerator, string filterArgument) { #region Require if (sourceEnumerator == null ) throw new ArgumentNullException(); if ( string .IsNullOrEmpty(filterArgument) == true ) throw new ArgumentNullException(); #endregion _sourceEnumerator = sourceEnumerator; _filterArgument = filterArgument; } public virtual void Dispose() { _sourceEnumerator.Dispose(); _currentResult = null ; } // Methods public string Current { get { return _currentResult; } } object System.Collections.IEnumerator.Current { get { return this .Current; } } public bool MoveNext() { while (_sourceEnumerator.MoveNext() == true ) { if (_sourceEnumerator.Current != _filterArgument) { _currentResult = _sourceEnumerator.Current; return true ; } } return false ; } public void Reset() { _sourceEnumerator.Reset(); _currentResult = null ; } } |
使用范例1 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static void Test002() { // 建立字符串数组,并且转型为IEnumerable string [] stringArray = new string [] { "A" , "B" , "C" }; IEnumerable< string > stringEnumerable = stringArray; // 生成并且套用StringFilterEnumerable对象,过滤字符串"B" stringEnumerable = new StringFilterEnumerable(stringEnumerable, "B" ); // 列举 IEnumerable使用 Foreach foreach ( string item in stringEnumerable) { Console.WriteLine(item); } } |
使用范例2 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static void Test002_1() { // 建立字符串数组,并且转型为IEnumerable string [] stringArray = new string [] { "A" , "B" , "C" }; IEnumerable< string > stringEnumerable = stringArray; // 生成并且套用StringFilterEnumerable对象,过滤字符串"B"+"A" stringEnumerable = new StringFilterEnumerable( new StringFilterEnumerable(stringEnumerable, "B" ), "A" ); // 列举 IEnumerable使用 Foreach foreach ( string item in stringEnumerable) { Console.WriteLine(item); } } |
3. 使用 IEnumerable的扩充方法生成套用 Decorator的 IEnumerable,方便串接程序。
这段就比较好理解,只是将上一个要点建立的使用扩充方法来做对象生成动作。
主要要达成的目的就是将一些程序做隐藏的动作,在使用的时候可以比较方便。
(更细节的数据,可以参考:[扩充方法 (C# 程序设计手册)])
原始码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static class StringEnumerableExtensions { public static IEnumerable< string > Filter( this IEnumerable< string > sourceEnumerable, string filterArgument) { #region Require if (sourceEnumerable == null ) throw new ArgumentNullException(); if ( string .IsNullOrEmpty(filterArgument) == true ) throw new ArgumentNullException(); #endregion return new StringFilterEnumerable(sourceEnumerable, filterArgument); } } |
使用范例1 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static void Test003() { // 建立字符串数组,并且转型为IEnumerable string [] stringArray = new string [] { "A" , "B" , "C" }; IEnumerable< string > stringEnumerable = stringArray; // 使用扩充方法套用StringFilterEnumerable对象,过滤字符串"B" stringEnumerable = stringEnumerable.Filter( "B" ); // 列举 IEnumerable使用 Foreach foreach ( string item in stringEnumerable) { Console.WriteLine(item); } } |
使用范例2 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static void Test003_1() { // 建立字符串数组,并且转型为IEnumerable string [] stringArray = new string [] { "A" , "B" , "C" }; IEnumerable< string > stringEnumerable = stringArray; // 使用扩充方法套用StringFilterEnumerable对象,过滤字符串"B"+"A" stringEnumerable = stringEnumerable.Filter( "B" ).Filter( "A" ); // 列举 IEnumerable使用 Foreach foreach ( string item in stringEnumerable) { Console.WriteLine(item); } } |
后记 :
依照前面章节的说明,细细去分析程序。
不难看出 LINQ的执行的时间点,不是在呼叫扩充方法的当下。
而是在使用foreach列举IEnumerable时,才去执行。
这也就是 LINQ的延迟执行(Deferred Execution)。
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?