第十九章 LINQ
- LINQ:语言集成查询(Language Intergrated Query)。它允许我们以SQL查询数据库的方式查询数据集合,使用它可以从数据库、程序对象集合和XML中查询数据。
- 匿名类型:通俗的说,匿名类型就是在对象初始化时不提供类型,在LINQ中会经常用到,如
var students = new {name = "automation",age = 16};
- 匿名类型只能和局部变量一块使用,不能作为类成员;由于匿名类型没有名字,需使用关键字var作为变量类型;不能设置匿名类型对象的属性,为匿名类型创建的属性是只读的。
- 匿名类型的成员有三种形式,赋值形式,成员访问表达式以及标识符,如果是后两种,就需要提前确定好它们的量。
var students = new {name = "automation",age = Person.Age,Major};
其中,Person.Age和Major需要提前定义好才可以。 - LINQ的两种语法形式:方法语法和查询语法,查询语法通俗易懂,方法语法是一组标准查询运算符的方法。查询语法是声明式的,它描述你想要返回的东西,但没有指明如何执行这个查询;方法语法是命令式的指明查询方法调用的顺序。
- 示例:
using System;
using System.Linq;
using System.Xml.Linq;
namespace test42
{
class Program
{
static void Main(string[] args)
{
int[] number = {15,16,18,36,56,81,100};
var numb1 = from num in number //给出数据源number
where num >= 18 //条件判断
select num; //选择元素
var numb2 = number.Where(num => num % 3 == 0); //Lambda表达式,方法语法
int count = number.Count(); //方法语法
foreach(var nu1 in numb1)
{
Console.WriteLine("nu1:{0}",nu1);
}
foreach(var nu2 in numb2)
{
Console.WriteLine("nu2:{0}",nu2);
}
Console.WriteLine("numb2的个数:{0}",count);
}
}
}
执行结果:
- 如果查询表达式返回的是枚举类型,知道处理枚举时才会执行,并且查询多少次枚举就会执行多少次,如果在执行查询之前枚举值有变化,会返回变化后的值,如果返回标量值,则查询会立即执行,并将结果保存在查询变量中。
- from子句:指定作为数据源的数据集合,引入迭代变量,来表示数据源的每一数据。语法格式为
from Type item in Items
其中,类型Type是可选的,item是迭代变量名,Items是要查询的集合名称,必须是可枚举的。from子句可以有任意多个,通常会与foreach()语句配合使用。 - join子句:将两个或多个数据进行结合;联结操作接受两个数据源,然后创建一个新的对象集合,集合中的元素包含原始数据源的所有字段。语法格式为
join Identifier in Collection on Filed1 equals Filed2
它是将多个集合中的元素符合条件的都筛选出来。 - join子句示例:
using System;
using System.Linq;
using System.Xml.Linq;
namespace test43
{
class Myclass1
{
public int[] num1 = {15,18,32,36,81,56,94};
}
class Myclass2
{
public int[] num2 = {12,16,25,36,45,21,75,81};
}
class Program
{
static void Main(string[] args)
{
Myclass1 mycl1 = new Myclass1();
Myclass2 mycl2 = new Myclass2();
var num = from mycll1 in mycl1.num1
join mycll2 in mycl2.num2 on mycll1 equals mycll2
into number //查询延续,将满足条件的mucll2加入新的集合number中
from numb in number
select numb; //以select语句结尾
foreach(var nub in num)
{
Console.WriteLine(nub);
}
}
}
}
执行结果是:36和81。
- let子句:接受一个表达式运算并将其赋值给一个变量,而这个变量又可以在接下来的语句中使用。示例:
using System;
using System.Linq;
using System.Xml.Linq;
namespace test43
{
class Myclass1
{
public int[] num1 = {15,18};
}
class Myclass2
{
public int[] num2 = {12,16,25};
}
class Program
{
static void Main(string[] args)
{
Myclass1 mycl1 = new Myclass1();
Myclass2 mycl2 = new Myclass2();
var num = from mycll1 in mycl1.num1 //给出数据源
from mycll2 in mycl2.num2
let number = mycll1 + mycll2 //let子句接受一个表达式
where number>=31
select new {mycll1,mycll2,number}; //select子句可以是一个数据项或者匿名类型
foreach(var nub in num) //显示结果
{
Console.WriteLine(nub);
}
}
}
}
执行结果:
- orderby子句: 就是对元素进行排序,默认是升序(ascending),也可以降序(descending)。示例:
using System;
using System.Xml.Linq;
using System.Linq;
namespace test44
{
class Program
{
static void Main(string[] args)
{
int[] number = {15,16,85,56,42,36,69};
var num = from numb in number
orderby numb //对元素排序
select numb;
foreach(var numb in num)
{
Console.Write("{0} ",numb);
}
}
}
}
执行结果:
- group…by子句:将select的对象按照一定的标准进行分组;如果项包含在查询结果中,他们可以根据某个字段的值分组,作为分组依据的属性叫键;group子句返回的不是原始数据项的枚举,而是项的分组的可枚举类型;分组本身是可枚举类型,可以枚举实际的项。
using System;
using System.Xml.Linq;
using System.Linq;
namespace test44
{
class Program
{
static void Main(string[] args)
{
var students = new[]
{
new {LName = "Kang",FName = "xiaoqiu",Age = 18,Major = "History"} ,
new {LName = "zhang",FName = "goddess",Age = 18,Major = "English"} ,
new {LName = "wei",FName = "Angel",Age = 18,Major = "History"}
};
var query = from stu in students
group stu by stu.Major;
foreach(var s in query)
{
Console.WriteLine("{0}",s.Key);
foreach(var v in s)
{
Console.WriteLine(" {0} {1}",v.LName,v.FName);
}
}
}
}
}
执行结果:
- 标准查询运算符:被查询的集合对象叫做序列,它必须实现IEnumerable接口;标准查询运算符使用方法语法,一共有47种;有些运算符返回可枚举类型,有些返回标量,返回标量的运算符会立即执行,并返回一个值;很多操作以谓词作为参数,谓词是一个方法,它以对象作为参数,根据对象是否满足条件返回true或false。
- 标准查询运算符的签名:System.Linq.Enumerable类声明了标准查询运算符方法,但这些方法不仅仅是一些方法,它们是扩展了IEnumerable泛型类的扩展方法。比如Count、First和Where方法签名如下,必须声明为public static形式Count方法返回序列中的所有元素个数,First方法返回序列中的第一个元素。
public ststic int Count<T>(this IEnumerable<T> source);
public ststic T First<T>(this IEnumerable<T> source);
public ststic IEnumerable<T> Where<T>(this IEnumerable<T> source,...);
int[] number = new int[]{12,16,18,21,25,36,45,56,81};
var countnum1 = Enumerable.Count(number);
var firstnum1 = Enumerable.First(number); //方法语法
var countnum2 = number.Count();
var firstnum2 = number.First(); //扩展方法
- 结合查询语句和方法语句:可以将查询语句作为一个整体运用在方法语句中。
var numbers = new {12,16,18,21,25,36,45,56,81};
var number = (from num in numbers
where num %3 == 0
select num).Count();
Console.WriteLine("The number can be divided by three: ");
foreach(var n in number)
{
Console.WriteLine("{0}",n);
}
- 将委托作为参数:将委托作为标准查询运算符的参数,可以为方法增加额外的功能,因为委托总是持有方法的。比如Count方法
public static int Count<T>(this IEnumerable<T>,Func<T,bool> func);
让Count方法统计能够被3整除的数
int[] numbers = new int[]{12,16,18,21,25,36,45,56,81};
int divnum = numbers.Count(x => x%3==0); //寻找整除3的Lambda表达式
- LINQ预定义的委托类型:有两种,Func委托和Action委托,各有17个成员,TR代表返回值,必须是类型参数列表的最后一项,Func委托的前四项为
public delegate TR Func<out TR>(); //没有方法参数
public delegate TR Func<in T1,out TR>(T1 a1);
public delegate TR Func<in T1,in T2,out TR>(T1 a1,T2 a2);
public delegate TR Func<in T1,in T2,in T3,out TR>(T1 a1,T2 a2,T3 a3);
Action委托的前四项没有返回值,也就没有返回值的类型参数,它们为
public delegate void Action ();
public delegate void Action<in T1>(T1 a1);
public delegate void Action<in T1,in T2>(T1 a1,T2 a2);
public delegate void Action<in T1,in T2,in T3>(T1 a1,T2 a2,T3 a3);
19.使用委托参数示例:
class Program
{
static bool IsOdd(int x) //委托对象持有的方法
{
return x%2 = =1;
}
static void Main()
{
int[] intArray = new int[]{16,15,25,36,48,63};
Func<int,bool> mydel = new Func<int,bool>(IsOdd); //创建委托对象
var countodd = intArray.Count(mydel); //执行委托
Console.WriteLine("Count of odd numbers:{0}",countodd);
}
}
- 使用Lambda表达式参数的示例:
class Program
{
static void Main()
{
int[] intArray = new int[]{16,15,25,36,48,63};
var countodd = intArray.Count(x => x%2==1);
Console.WriteLine("Count of odd numbers:{0}",countodd);
}
}
也可以使用匿名方法代替Lambda表达式,但是显得复杂些
class Program
{
static void Main()
{
int[] intArray = new int[]{16,15,25,36,48,63};
Func<int,bool> mydel = delegate(int x)
{
return x%2 == 1;
};
var countodd = intArray.Count(mydel);
Console.WriteLine("Count of odd numbers:{0}",countodd);
}
}