【翻译】Pro LINQ Language Integrated Query in C# 2008 -- 第三章 (LINQ TO Objects) 第二节

返回 IEnumerable<T>, Yielding, 后期执行查询

要记住这个重点,许多标准查询操作是一个返回 IEnumerable<T> 的原型,并且我们认为 IEnumerable<T> 是一个序列,如果操作实际上不返该序列时,我们将该操作称为调用。当枚举生成一个序列的项时操作返回一个对象。它是在枚举返回对象过程的中,该查询实际上才执行,并生成一个序列输出项。这个方法的查询称为后执行查询。

当我使用批量生成时,你并不知道,我是指关于C# 2.0 生成关键字,新增到C# 语言中使得书写枚举更加容易。

例如,示例 3-2 代码

示例 3-2. 简单查询

string[] presidents = {
    "Adams""Arthur""Buchanan""Bush""Carter""Cleveland",
    "Clinton""Coolidge""Eisenhower""Fillmore""Ford""Garfield",
    "Grant""Harding""Harrison""Hayes""Hoover""Jackson",
    "Jefferson""Johnson""Kennedy""Lincoln""Madison""McKinley",
    "Monroe""Nixon""Pierce""Polk""Reagan""Roosevelt""Taft",
    "Taylor""Truman""Tyler""Van Buren""Washington""Wilson"};
IEnumerable
<string> items = presidents.Where(p => p.StartsWith("A"));
foreach(string item in items)
    Console.WriteLine(item);

当代码行中包含使用 Where 操作的查询时,该行并没有被执行。而是返回一个对象。 它是在枚举返回对象的过程中,该查询实际上才执行。这意味着就有可能,直到输出序列枚举时,查询自身中发生的错误可能没有及时被检测到。

注意:直到输出序列枚举时查询中的错误可能都不会被检测到。

运行结果:

Adams
Arthur

该查询按照预期方式执行。但是,我会有意加入一个错误。下面的代码将尝试检索每个President的名称的第五个字符的索引。当枚举游标到一个长度少于五个字符项时,一个错误将出现。请记住,直到输出序列枚举时,这个错误都将不会发生。请看示例 3-3

示例 3-3. 一个含有异常的简单示例查询

string[] presidents = {
    "Adams""Arthur""Buchanan""Bush""Carter""Cleveland",
    "Clinton""Coolidge""Eisenhower""Fillmore""Ford""Garfield",
    "Grant""Harding""Harrison""Hayes""Hoover""Jackson",
    "Jefferson""Johnson""Kennedy""Lincoln""Madison""McKinley",
    "Monroe""Nixon""Pierce""Polk""Reagan""Roosevelt""Taft",
    "Taylor""Truman""Tyler""Van Buren""Washington""Wilson"};
IEnumerable
<string> items = presidents.Where(s => Char.IsLower(s[4]));
    Console.WriteLine(
"After the query.");
foreach (string item in items)
    Console.WriteLine(item);

这段代码编译是正常的,但是运行后的结果如下:

After the query.
Adams
Arthur
Buchanan
Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds
of the array.

注意该查询的输出结果。结果输出到第四项时异常发生。只是因为一个查询编译,并看上去运行并没有错误,不假设该查询是bug-free。

另外,因为这些返回 IEnumerable<T> 类型的查询是后期执行的,你可以调用代码来定义该查询。你使用枚举多次结果时,如果数据被改变,每次你枚举结果,你将获取不同结果。示例 3-4 展示了一个后期执行查询,不缓存该查询结果并在下一次枚举结果时改变数据的示例。

示例 2-4. 一个在枚举结果之间改变查询结果的示例。

// Create an array of ints.
int[] intArray = new int[] { 1,2,3 };
IEnumerable
<int> ints = intArray.Select(i => i);
// Display the results.
foreach(int i in ints)
    Console.WriteLine(i);
// Change an element in the source data.
intArray[0= 5;
Console.WriteLine(
"---------");
// Display the results again.
foreach(int i in ints)
    Console.WriteLine(i);

在我的说明中将得到更多技术,使得发生什么样的情况都将变的透明。当我调用Select操作时,返回一个存储了实现 IEnumerable<int> 类型的 ints 变量对象。到这里,实际上查询还没有执行,但是查询存储在 ints 的对象中。从技术上来讲,既然查询没有执行,实际上一个整形序列也不存在,但是在这种 Select 操作情况下,ints 对象知道如何通过执行查询分配给序列。

当我使用 foreach 语句第一次循环 ints 时,ints 执行查询并获取序列中的一个元素。

下来我在原整数数组中改变一个项目的值。然后我再次调用 foreach 语句循环。这将导致 ints 来再次执行该查询。因为我改变了原数组的项目值,并且因为 ints 是又一次被枚举,所以又一次执行查询,改变的项被返回。

运行结果如下:

1
2
3
---------
5
2
3

注意:即使我只调用查询一次,枚举后的结果都是不相同的。这进一步证实,该查询是后期执行的。如果他不是,这两个枚举的结果将会是相同的。这是有利弊的。如果你不希望操作后期执行,使用一个不返回 IEnumerable<T> 类型的转换操作, 使得这个查询不能后期执行(例如使用 ToArray、ToList、ToDictionary、ToLookup 来创建一个不同的数据结构)。如果数据源改变,缓存结果将不会改变。

示例 3-5 是在示例 3-4 中修改了所有查询返回一个 IEnumerable<int> 为 调用 ToList 操作返回一个 List<int> 。

示例 3-5. 返回一个 List,使得查询立即执行并返回缓存结果。

// Create an array of ints.
int[] intArray = new int[] { 123 };
List
<int> ints = intArray.Select(i => i).ToList();
// Display the results.
foreach(int i in ints)
    Console.WriteLine(i);

// Change an element in the source data.
intArray[0= 5;
Console.WriteLine(
"---------");
// Display the results again.
foreach(int i in ints)
    Console.WriteLine(i);

运行结果如下:
1
2
3
---------
1
2
3

注意:第二个枚举的结果和第一个枚举的结果相同。这是因为 ToList 方法不是后期执行的,并且实际上查询被立即执行调用。

一个技术讨论 什么是不同的 示例 3-5 和示例3-6 两者的返回值不同,在示例 3-5 中 Select 操作仍然是后期执行,但ToList 操作不是。在查询语句中当 ToList 操作被调用,返回 Select 操作立即执行的枚举对象,即整个查询被执行。

因为最近比较忙可能后面的内容翻译的将会缓慢,请大家谅解。

转载于:https://www.cnblogs.com/winnerzone/archive/2008/04/09/1144268.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值