Linq
语言集成查询 (LINQ),是.NET做出来的一种,希望在程序中能实现类似于数据库查询的功能。
在数据库中,假如一个库的数据有1000G数据。希望在其中找出一个名字叫张三的人。
如何查找呢?这么庞大的数据量是不可能全在程序运行时加载进内存。
所以他会分片。一次只读取一块数据,如果满足就留下放到内存,如果不满足就读下一块数据。
c#中的Linq类似于此效果。
如果你只要求找出符合条件的一个数据。
那么Linq一旦找到了一个数据,就会终止对数据的遍历。以此达到优化。
虽然手写for循环,再进行一点操作也能达到这样的效果。
但是使用Linq达到同样的效果更简便。
查询表达式(常用部分)
一个最简单的查询表达式长这样
int[] p = { 1, 2, 3, 4, 5 };
var e = from a in p select a * a;
这个表达式返回的值是IEnumerable类型,可以使用foreach遍历。
foreach (var item in e)
{
Console.WriteLine(item);
}
查询表达式中声明的标识符,作用域仅限于这个查询表达式。
获取数据源
表达式中的from a in p
是说,从变量p中遍历元素。把p中的元素称为a。
其中,数据源必须实现IEnumerable接口,而不像foreach只需要有个GetEnumerator方法。
映射
表达式中的select a*a
是对元素进行映射。
可以理解为,把元素a进行以下操作(a*a),得到的结果组合成一个新序列。
查询表达式中必须以映射或分组结尾。
筛选
int[] p = { 1, 2, 3, 4, 5 };
var e = from a in p
where a > 3 || a % 2 == 0
select a * a;
where子句可以筛选元素。
如果元素不满足条件,那么不会再经过后续的操作。
例如,序列中的1,3不满足条件,不会被映射,也就不会出现在结果序列中。
排序
int[] p = { 1, 6, 2, 5, 3 };
var e = from a in p
where a > 3 || a % 2 == 0
orderby a
select a * a;
orderby子句会根据元素映射出来的键进行排序。
这个临时映射的键只作用于这次排序的操作,不会影响最后查询的结果。
orderby子句默认以升序排序(从小开始,越往后越大)
如果要改为降序排序,在键的映射后加descending
string[] s = { "hello", "WWW.com", "baidu", "remove" };
var des = from a in s
orderby a.Length descending
select a;
如果有多个排序条件,请使用元组或匿名类。
直接再次使用orderby子句会打乱已经排好的顺序。
string[] s = { "hello", "WWW.com", "baidu", "remove" };
var des = from a in s
orderby (a.Length, a[0]) descending
select a;
查询表达式(剩余部分)
延续
一个查询表达式在映射或分组后就结束了。
如果还需要继续查询,需要把映射或分组后的结果值声明为新的临时值。
string[] s = { "hello", "WWW.com", "baidu", "remove" };
var des = from a in s
select new { len = a.Length, fir = a[0] } into b
orderby b.len descending
select b.fir;
临时值
let 可以创建一个临时值。
可以代替映射元组。
string[] s = { "hello", "WWW.com", "baidu", "remove" };
var des = from a in s
let len=a.Length
let fir = a[0]
orderby len descending
select fir;
分组
分组查询会返回一个 IGrouping类型的序列。
这个接口继承IEnumerable,而比他多出来的属性只有一个:Key
这个Key是这个序列中共有的键。
string[] s = { "hello", "WWW.com", "baidu", "remove" };
var des = from a in s
orderby a.Length
group a by a.Length;
foreach (var item in des)
{
Console.WriteLine("接下来的元素的公有键是"+item.Key);
foreach (var item2 in item)
{
Console.WriteLine(item2);
}
Console.WriteLine();
}
在group子句中,by指示分组依据。
例如hello和baidu的Length属性都是5。
那么他们就会被分到同一个IGrouping中。
并且这个IGrouping的Key值会赋值为5。
group a by a.Length
中的a也表示一个映射。
在分完组后返回的值就是IGrouping了,没法直接操控里面的元素了。
分组中的映射是最后直接控制元素的时机。
联表
如果两个序列中有关联数据。
使用联表可以把这两个序列合并成一个序列。
假如一个老师拿到了附近网吧的登记单,想从中找出自己学生的上网情况。
class 学生
{
public string 姓名;
public string 身份证号;
}
class 网吧登记名单
{
public string 身份证号;
public int 机位;
}
学生[] 班级名单 = new 学生[]
{
new 学生{ 姓名="张三" ,身份证号="186" },
new 学生{ 姓名="李四" ,身份证号="103" },
new 学生{ 姓名="王五" ,身份证号="197" },
new 学生{ 姓名="赵六" ,身份证号="208" }
};
网吧登记名单[] 极速网吧 = new 网吧登记名单[]
{
new 网吧登记名单{ 身份证号="103" ,机位=47 },
new 网吧登记名单{ 身份证号="197" ,机位=36 },
new 网吧登记名单{ 身份证号="248" ,机位=33 },
new 网吧登记名单{ 身份证号="103" ,机位=32 }
};
var t = from a in 班级名单
join b in 极速网吧 on a.身份证号 equals b.身份证号
select a.姓名;
join子句中,前半段和from一样,都是指示数据源并对元素命名。
后半段是指示关联关系。这里是要求a的身份证号和b的身份证号对应。
这里面的equals是一个关键字。他的意思是你的Equals方法可能乱写,不能用。
他要使用linq自己的判断方法。
如果一个元素可以和多个元素匹配,那么会生成多个组合元素。例如:
string[] 花色 = { "黑桃","红桃","梅花","方片"};
string[] 点数 = { "A","2","3","4","5","6","7","8","9","10","J","Q","K"};
var p = from a in 花色
join b in 点数 on 1 equals 1
select a + b;
由于equals关系式用了两个和元素无关的量,使得恒成立。
最后每个元素之间都会配对出一个组合值。