21.5.7 查询中的匿名类型
21.5.8 group 子句
21.5.9 查询延续
21.6 标准查询运算符
21.6.3 委托作为参数
21.6.4 LINQ预定义的委托类型
21.6.5 使用委托参数的示例
21.6.6 使用 Lambda 表达式参数的示例
select new { s.LastName, s.FirstName, s.Major };
group子句把select的对象根据一些标准分组。
group student by student.Major ;
如果项目包含在查询的结果中,它们就可以根据某个字段的值进行分组。作为分组一句的项叫做键(Key)。
static void Main() { var students = new[] // Array of objects of an anonymous type { new { LName="Jones", FName="Mary", Age=19, Major="History" }, new { LName="Smith", FName="Bob", Age=20, Major="CompSci" }, new { LName="Fleming", FName="Carol", Age=21, Major="History" } }; var query = from student in students group student by student.Major; foreach (var s in query) // Enumerate the groups. { Console.WriteLine("{0}", s.Key); foreach (var t in s) // Enumerate the items in the group. Console.WriteLine(" {0}, {1}", t.LName, t.FName); } }
查询延续子句可以接受查询的一部分结果并赋予一个名字,从而可以在查询的另一部分中使用。查询延续的语法如下。
查询连接了groupA和groupB,并且命名为groupAandB,然后从groupA和groupB中进行一个简单的select。
static void Main() { var groupA = new[] { 3, 4, 5, 6 }; var groupB = new[] { 4, 5, 6, 7 }; var someInts = from a in groupA join b in groupB on a equals b into groupAandB //查询继续 from c in groupAandB select c; foreach (var a in someInts) Console.Write("{0} ", a); }
标准查询运算符由一系列的API的方法组成,它能让我们查询任何.NET数组和集合。
1.被查询的集合对象叫序列,它必须实现 IEnumerable<T> 接口,T是类型。
2.标准查询运算符使用方法语法。
3.一些运算符返回IEnumerable对象(或其他序列),而其他的一些运算符返回标量。返回标量的运算符立即执行,而返回替代可枚举类型对象的值会被延迟迭代。
class Program { static int[] numbers = new int[] { 2, 4, 6 }; static void Main() { int total = numbers.Sum(); int howMany = numbers.Count(); // ↑ ↑ ↑ //标量对象 序列 运算符 Console.WriteLine("Total: {0}, Count: {1}", total, howMany); } }
查询表达式Count的定义:
每一个运算符的第一个参数是IEnumerable<T>对象的引用,之后是参数可以是任何类型。很多运算符接受泛型委托作为参数。泛型委托用于给用户提供自定义的代码。
为了解释,首先从演示 Count 运算符的几种使用方法开始。Count 运算符被重载并且有两种形式。第一种,
public static int Count<TSource>(this IEnumerable<TSource> source);
和所有扩展方法差不多,我们可以使用标准静态方法形式或在一个扩展类上使用实例方法的形式,如下:
var count1 = System.Linq.Enumerable.Count(intArry); // 静态方法形式
var count2 = intArry.Count(); // 实例方法形式
查询统计给定的整数数组中int的总数。然而,我们希望看看数组中奇数元素的总数,我们必须指定 Count 方法的第二种形式才能实现,如下所示。它有一个泛型委托作为其第二个参数。调用时,我们必须提供一个接受单个 TSource 类型的输入参数并返回布尔值的委托对象。委托代码的返回值必须指定元素是否包含在总数中。
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
例如,通过一个lambda表达式在输入值是奇数时返回true,否则返回false。如果是奇数,Count会把这个元素包含在总数中。
static void Main() { int[] intArray = new int[] { 3, 4, 5, 6, 7, 9 }; var countOdd = intArray.Count(n => n % 2 == 1); Console.WriteLine("Count of odd numbers: {0}", countOdd); }
3,5,7,9是奇数
Count of odd numbers: 4
请按任意键继续. . .
LINQ定义了两套泛型委托类型与标准查询运算符一起使用。它们就是 Func 委托及 Action 委托,各有17个成员。
(翻译Func:函数、功能)
1.TR带表了返回值,并且总是在类型参数列表的最后一个。
2.注意返回类型参数有一个out,使之可以协变,也就是说可以接受声明的类型或从这个类型派生的类型。
3.输入参数有一个in,使之可以逆变,也就是你可以接受声明的类型或从这个类型派生的类型。
我们再来看看Count的声明。发现第二个参数必须是委托对象,它接受单个T类型的参数作为方法参数并返回一个bool类型值。
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate); // ↑ ↑ // 参数类型 返回类型
产生一个布尔值的参数委托叫做谓词。
下面是4个Action委托。只是没有返回值,和Func委托相似。所有参数类型都是逆变。
如下面代码我们先声明IsOdd方法,它接受单个int类型的参数,并且返回表示输入参数是否为奇数的bool值。Main方法做了如下事情。
1.声明了int数组作为数据源。
2.它创建了一个类型为Func<int,bool>名称为MyDel的委托对象,并且使用IsOdd方法来初始化委托对象。注意,我们不需要声明Func委托类型,因为LINQ已经预定义了。
3.它使用委托对象调用Count。
static bool IsOdd(int x) // Method to be used by the delegate object { return x % 2 == 1; // Return true if x is odd. } static void Main() { int[] intArray = new int[] { 3, 4, 5, 6, 7, 9 }; Func<int, bool> myDel = new Func<int, bool>(IsOdd); // Delegate object var countOdd = intArray.Count(myDel); // Use delegate Console.WriteLine("Count of odd numbers: {0}", countOdd); }
如果方法在其他地方被调用,而不仅仅是用来初始化委托对象,前面的方法很好,然而,如果希望更简洁和更局部化的方法,就使用Lambda运算符。
static void Main() { int[] intArray = new int[] { 3, 4, 5, 6, 7, 9 }; var countOdd = intArray.Count(x => x % 2 == 1); Console.WriteLine("Count of odd numbers: {0}", countOdd); }