# 使用 LINQWorking with LINQ

## 介绍Introduction

• 如何使用 LINQ 生成序列。How to generate sequences with LINQ.
• 如何编写可轻松用于 LINQ 查询的方法。How to write methods that can be easily used in LINQ queries.
• 如何区分及早计算和惰性计算。How to distinguish between eager and lazy evaluation.

## 创建数据集Creating the Data Set

// Program.cs
using System;
using System.Collections.Generic;
using System.Linq;


// Program.cs
// The Main() method

static IEnumerable<string> Suits()
{
yield return "clubs";
yield return "diamonds";
yield return "hearts";
}

static IEnumerable<string> Ranks()
{
yield return "two";
yield return "three";
yield return "four";
yield return "five";
yield return "six";
yield return "seven";
yield return "eight";
yield return "nine";
yield return "ten";
yield return "jack";
yield return "queen";
yield return "king";
yield return "ace";
}


// Program.cs
static void Main(string[] args)
{
var startingDeck = from s in Suits()
from r in Ranks()
select new { Suit = s, Rank = r };

// Display each card that we've generated and placed in startingDeck in the console
foreach (var card in startingDeck)
{
Console.WriteLine(card);
}
}


var startingDeck = Suits().SelectMany(suit => Ranks().Select(rank => new { Suit = suit, Rank = rank }));


## 控制顺序Manipulating the Order

// Program.cs
public static void Main(string[] args)
{
var startingDeck = from s in Suits()
from r in Ranks()
select new { Suit = s, Rank = r };

foreach (var c in startingDeck)
{
Console.WriteLine(c);
}

// 52 cards in a deck, so 52 / 2 = 26
var top = startingDeck.Take(26);
var bottom = startingDeck.Skip(26);
}


// Extensions.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqFaroShuffle
{
public static class Extensions
{
public static IEnumerable<T> InterleaveSequenceWith<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
// Your implementation will go here soon enough
}
}
}


public static IEnumerable<T> InterleaveSequenceWith<T> (this IEnumerable<T> first, IEnumerable<T> second)


IEnumerable<T> 接口有一个方法 (GetEnumerator)。The IEnumerable<T> interface has one method: GetEnumerator. GetEnumerator 返回的对象包含用于移动到下一个元素的方法，以及用于检索序列中当前元素的属性。The object returned by GetEnumerator has a method to move to the next element, and a property that retrieves the current element in the sequence. 将使用这两个成员来枚举集合并返回元素。You will use those two members to enumerate the collection and return the elements. 由于此交错方法是迭代器方法，因此将使用上面的 yield return 语法，而不用生成并返回集合。This Interleave method will be an iterator method, so instead of building a collection and returning the collection, you'll use the yield return syntax shown above.

public static IEnumerable<T> InterleaveSequenceWith<T>
(this IEnumerable<T> first, IEnumerable<T> second)
{
var firstIter = first.GetEnumerator();
var secondIter = second.GetEnumerator();

while (firstIter.MoveNext() && secondIter.MoveNext())
{
yield return firstIter.Current;
yield return secondIter.Current;
}
}


// Program.cs
public static void Main(string[] args)
{
var startingDeck = from s in Suits()
from r in Ranks()
select new { Suit = s, Rank = r };

foreach (var c in startingDeck)
{
Console.WriteLine(c);
}

var top = startingDeck.Take(26);
var bottom = startingDeck.Skip(26);
var shuffle = top.InterleaveSequenceWith(bottom);

foreach (var c in shuffle)
{
Console.WriteLine(c);
}
}


## 比较Comparisons

public static bool SequenceEquals<T>
(this IEnumerable<T> first, IEnumerable<T> second)
{
var firstIter = first.GetEnumerator();
var secondIter = second.GetEnumerator();

while (firstIter.MoveNext() && secondIter.MoveNext())
{
if (!firstIter.Current.Equals(secondIter.Current))
{
return false;
}
}

return true;
}


// Program.cs
static void Main(string[] args)
{
// Query for building the deck

// Shuffling using InterleaveSequenceWith<T>();

var times = 0;
// We can re-use the shuffle variable from earlier, or you can make a new one
shuffle = startingDeck;
do
{
shuffle = shuffle.Take(26).InterleaveSequenceWith(shuffle.Skip(26));

foreach (var card in shuffle)
{
Console.WriteLine(card);
}
Console.WriteLine();
times++;

} while (!startingDeck.SequenceEquals(shuffle));

Console.WriteLine(times);
}


## 优化Optimizations

shuffle = shuffle.Skip(26).InterleaveSequenceWith(shuffle.Take(26));


Extensions.cs 文件中输入或复制下面的方法。In your Extensions.cs file, type in or copy the method below. 此扩展方法会在项目目录中新建一个名称为 debug.log 的文件，并将当前正在执行的查询记录到日志文件中。This extension method creates a new file called debug.log within your project directory and records what query is currently being executed to the log file. 可以将此扩展方法追加到任何查询中，以标记查询已执行。This extension method can be appended to any query to mark that the query executed.

public static IEnumerable<T> LogQuery<T>
(this IEnumerable<T> sequence, string tag)
{
// File.AppendText creates a new file if the file doesn't exist.
using (var writer = File.AppendText("debug.log"))
{
writer.WriteLine(\$"Executing Query {tag}");
}

return sequence;
}


using System.IO;


// Program.cs
public static void Main(string[] args)
{
var startingDeck = (from s in Suits().LogQuery("Suit Generation")
from r in Ranks().LogQuery("Rank Generation")
select new { Suit = s, Rank = r }).LogQuery("Starting Deck");

foreach (var c in startingDeck)
{
Console.WriteLine(c);
}

Console.WriteLine();
var times = 0;
var shuffle = startingDeck;

do
{
// Out shuffle
/*
shuffle = shuffle.Take(26)
.LogQuery("Top Half")
.InterleaveSequenceWith(shuffle.Skip(26)
.LogQuery("Bottom Half"))
.LogQuery("Shuffle");
*/

// In shuffle
shuffle = shuffle.Skip(26).LogQuery("Bottom Half")
.InterleaveSequenceWith(shuffle.Take(26).LogQuery("Top Half"))
.LogQuery("Shuffle");

foreach (var c in shuffle)
{
Console.WriteLine(c);
}

times++;
Console.WriteLine(times);
} while (!startingDeck.SequenceEquals(shuffle));

Console.WriteLine(times);
}


public static void Main(string[] args)
{
var startingDeck = (from s in Suits().LogQuery("Suit Generation")
from r in Ranks().LogQuery("Value Generation")
select new { Suit = s, Rank = r })
.LogQuery("Starting Deck")
.ToArray();

foreach (var c in startingDeck)
{
Console.WriteLine(c);
}

Console.WriteLine();

var times = 0;
var shuffle = startingDeck;

do
{
/*
shuffle = shuffle.Take(26)
.LogQuery("Top Half")
.InterleaveSequenceWith(shuffle.Skip(26).LogQuery("Bottom Half"))
.LogQuery("Shuffle")
.ToArray();
*/

shuffle = shuffle.Skip(26)
.LogQuery("Bottom Half")
.InterleaveSequenceWith(shuffle.Take(26).LogQuery("Top Half"))
.LogQuery("Shuffle")
.ToArray();

foreach (var c in shuffle)
{
Console.WriteLine(c);
}

times++;
Console.WriteLine(times);
} while (!startingDeck.SequenceEquals(shuffle));

Console.WriteLine(times);
}


## 结束语Conclusion

• 使用 LINQ 查询，来将数据聚合到有意义的序列中using LINQ queries to aggregate data into a meaningful sequence
• 编写扩展方法，来将自己的自定义功能添加到 LINQ 查询writing Extension methods to add our own custom functionality to LINQ queries
• 查找 LINQ 查询可能会在其中遇到性能问题（如速度下降）的代码区域locating areas in our code where our LINQ queries might run into performance issues like degraded speed
• 与 LINQ 查询相关的延迟计算和及早计算，以及它们对查询性能的影响lazy and eager evaluation in regards to LINQ queries and the implications they might have on query performance