[.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)。

posted @   Clark159  阅读(366)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示