【翻译】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 [] {  1 2 3  };
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 操作立即执行的枚举对象,即整个查询被执行。

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

你可能感兴趣的:(【翻译】Pro LINQ Language Integrated Query in C# 2008 -- 第三章 (LINQ TO Objects) 第二节)