前面学习了LINQ查询表达式,现在我们来学习一下LINQ查询操作(如果说LINQ查询表达式类似SQL基本语句的话,LINQ查询操作就类似与SQL里的函数)。LINQ提供了数十个查询操作,大多数操作都在实现了IEnumerable<T>或IQueryable<T>的序列上运行(之所以加IQueryable<T>是因为其也是继承自IEnumerable<T>,事实上),并且返回的序列是一个IEnumerable<T>类型或继承自IEnumerable<T>类型的序列(除少数不是返回集合类型的操作外,排序操作返回的是IOrderedEnumerable<T>类型的集合(继承自IEnumerable<T>),后面将学的LINQ to SQL一般返回的是IQueryable<T>,但要知道它也是继承自IEnumerable<T>),对于那些没有实现IEnumerable<T>或IQueryable<T>的序列则不能用其中大多数查询操作,对于它们能用的那小部分查询操作,返回的也是一个IEnumerable<T>类型的序列。就像我们前面学习LINQ查询表达式时的注,表达式必须返回一个直接或间接实现了IEnumerable<T>接口的集合,而from # in *中,*必须是一个直接或间接实现了IEnumerable<T>接口的集合(而SQLServer数据表,数组,List<T>,还有LINQ to SQL时的Table<T>等都实现了IEnumerable<T>接口)下面我们来看一些:
http://www.code-design.cn/blogdetail2610.html
public class Person
{ private int id; public int Id { get { return id; } set { id = value; } } private string name; public string Name { get { return name; } set { name = value; } } private int age; public int Age { get { return age; } set { age = value; } } }
1.筛选操作Where
它和前面讲LINQ查询表达式时讲的where子句功能相似,起到筛选的作用,还是在原来的序列里筛选元素,所以筛选出的元素的类型不变。
protected void Button1_Click(object sender, EventArgs e)
{ List<Person> person = new List<Person>(); for (int i = 0; i < 10; i++) { person.Add(new Person {Id=i,Name=i.ToString()+"Dog",Age=i}); } //在LINQ查询操作的方法中,需要大量的使用Lambda表达式,说明了Lambda表达式的重要性。 var result = person.Where(u=>u.Id>7); //由于前面指明了数据源person(内元素是Person类型),所以编译器会为Lambda里的u指定为Person类型,Where查询操作返回的序列类型是IEnumerable<Person>,然后遍历得到最终的Person类型的元素。 foreach (var item in result) { Label1.Text += item.Name; } }
2.投影操作
Select
它与前面讲LINQ查询表达式时讲的select子句功能相似,返回的序列类型不一定就是原来序列的类型,具体是什么类型由里面的Lambda表达式决定,可以将鼠标移到查询操作上看看返回的是什么类型的序列(IEnumerable<T>)。
protected void Button2_Click(object sender, EventArgs e) { List<Person> person = new List<Person>(); for (int i = 0; i < 10; i++) { person.Add(new Person { Id = i, Name = i.ToString() + "select", Age = i }); } var result = person.Select(u=>u.Id.ToString()+"ABCD"); //鼠标移到此处可以看到返回的序列的类型是IEnumerable<String> foreach (var item in result) { Label2.Text += item; } }
SelectMany
与Select差不多,不过该函数更能更强一些,可以将多个序列合并成一个序列
而Select却不能,如果把下面改成var result = ints.Select(u=>u); 在LinqPad中可以很清楚的看出区别
SelectManySelect
protected void Button3_Click(object sender, EventArgs e)
{ int[] ints1 =new int[]{1,2,3,4,5}; int[] ints2 = new int[]{6,7,8,9,10}; List<int[]> ints = new List<int[]>(); ints.Add(ints1); ints.Add(ints2); var result =ints.SelectMany(u =>u); foreach (var item in result) { Label3.Text += item; } }
3.排序操作(按主要关键字或次要关键字排序,第一个排序关键字为主要关键字,第二个排序关键字为次要关键字)
OrderBy
根据关键字进行升序排列
protected void Button4_Click(object sender, EventArgs e)
{
int[] ints = new int[] { 2,4,3,9,7,5,8,10};
var result = ints.OrderBy(i => i);
foreach (var item in result)
{
Label4.Text += item.ToString();
}
}
OrderByDescending
根据关键字进行降序排列
protected void Button5_Click(object sender, EventArgs e)
{
int[] ints = new int[] { 2, 4, 3, 9, 7, 5, 8, 10 };
var result = ints.OrderByDescending(i => i);
foreach (var item in result)
{
Label5.Text += item.ToString();
}
}
ThenBy,ThenByDescending
根据次要关键字进行升序(降序)排列(前面必须有OrderBy或OrderByDescending排序操作)
protected void Button7_Click(object sender, EventArgs e)
{
List<Person> person = new List<Person>();
for (int i = 0; i < 10; i++)
{
person.Add(new Person { Id = i, Name = i.ToString() + "thenby", Age = i });
}
var result = person.OrderBy(u => u.Name).ThenByDescending(u => u.Id);
foreach (var item in result)
{
Label7.Text += item.Name;
}
}
Reverse
将序列中的元素进行反转(并不需要Lambda表达式)
protected void Button8_Click(object sender, EventArgs e)
{
int[] ints = new int[] { 2, 4, 3, 9, 7, 5, 8, 10 };
var result = ints.Reverse();
foreach (var item in result)
{
Label8.Text += item.ToString();
}
}
4.聚合操作
Count,LongCount
计算序列中的元素个数或序列中满足条件的元素(Lambda表达式可以是筛选作用的表达式)的个数,后者一般用于大型集合
Max,Min
计算序列中的最大(最小)值,也能计算用Lambda表达式处理后序列中每个元素后的最大(最小)值,但Lambda表达式不能是筛选作用的表达式(如u=>u>1等),因为只有where查询操作具有筛选作用,前面提到的select里的Lambda表达式也不能是筛选作用
Sum,Average
与Max,Min同类型,求和及平均值
Aggregate
http://www.cnblogs.com/chenxizhang/archive/2010/01/03/1638140.html
对集合中的元素进行自定义的聚合计算(Lambda表达式有俩参数(a,b)=>a?b,a先默认是序列的第一个元素,然后?第二个元素,将得到的结果再?下一个元素(第三个元素),直到?完所有元素,返回最终结果,?为任意运算)
protected void Button9_Click(object sender, EventArgs e)
{
int[] ints = new int[] { 2, 4, 3, 9, 7, 5, 8, 10 };
var result1= ints.Count();
var result2 = ints.Count(i => i > 4 && i < 9);
var result3 = ints.Max();
var cuowu = ints.Max(i => i > 1); //只是返回true或false
var result4 = ints.Max(i=>i*2); //处理每个元素再求,以下sum,Average可以这样
var result5 = ints.Sum();
var result6 = ints.Average();
double[] doubles = new double[] {2,4,10,100};
var result7 = doubles.Aggregate((a,b)=>a/b); //求2/4/10/100
Label9.Text = "个数one" + result1.ToString() +
"个数two" + result2.ToString() +
"最大值one" + result3.ToString() +
"最大值two" + result4.ToString() +
"和" + result5.ToString() +
"平均" + result6.ToString() +
"Aggregate"+result7.ToString();
}
5.集合操作
http://msdn.microsoft.com/zh-cn/library/system.collections(v=VS.80).aspx
与集合有关的类和接口位于System.Collections命名空间内
http://msdn.microsoft.com/zh-cn/library/system.collections.generic(v=VS.80).aspx
我们经常使用的List<T>等泛型类及泛型接口位于System.Collections.Generic命名空间内
http://www.cnblogs.com/xioxu/archive/2008/05/06/1185499.html(部分与集合有关的类和接口)
http://www.cnblogs.com/atravellers/archive/2010/03/02/1676174.html
(IEnumerable<T>,IEnumerator<T>,List<T>,ArrayList,数组)
http://www.cnblogs.com/szp1118/archive/2010/09/01/1814978.html
(使自定义集合类支持索引器(类似于属性,http://msdn.microsoft.com/zh-cn/library/6x16t2tx(VS.80).aspx),支持
foreach语句,支持初始化器)
http://www.51obj.cn/?p=810(索引器,枚举数IEnumerator(使用foreach),迭代器Iterator)
在.NET提供的集合类型中,任何集合类型及数组都可以使用foreach,因为它们都直接或间接实现IEnumerator/IEnumerable接口。(数组实现了IEnumerable<T>而IEnumerable<T>实现了IEnumerable)
Distinct
去掉数据源的重复的元素,并返回一个新序列。还可以指定一个比较器来比较两个元素是否相同。
Except
计算两个集合的差集(属于第一个集合而不属于第二个集合)
Intersect
计算两个集合的交集
Union
计算两个集合的并集
protected void Button10_Click(object sender, EventArgs e)
{
string Distinct="";
string Except="";
string Intersect = "";
string Union = "";
List<string> list1 = new List<string>();
list1.Add("A");
list1.Add("A");
list1.Add("B");
list1.Add("B");
List<string> list2 = new List<string>();
list2.Add("A");
list2.Add("B");
list2.Add("C");
var distinct = list1.Distinct(); //返回一个IEnumerable<T>类型的集合
var except = list2.Except(list1); //差集:属于前面的集合而不属于后面的集合
var intersect = list1.Intersect(list2);
var union = list1.Union(list2);
var diyi = distinct.ElementAt(0); //IEnumerable<T>类型的集合不能使用索引器,但有ElementAt方法
foreach (var item in distinct)
{
Distinct += item;
}
foreach (var item in except)
{
Except += item;
}
foreach (var item in intersect)
{
Intersect += item;
}
foreach (var item in union)
{
Union += item;
}
Label10.Text ="list1的第一个元素:"+distinct.ElementAt(0)+"Distinct:"+Distinct+"Except:"+Except+"Intersect:"+Intersect+"Union"+Union;
}
6.元素操作
ElementAt,ElementAtOrDefault
都返回集合中指定索引处的元素,后者在索引超出集合范围时返回默认值。
First,FirstOrDefault
都返回集合中第1个元素,或返回集合中满足指定条件的第1个元素,后者在不存在满足条件的元素时返回默认元素。
Last,LastOrDefault
都返回集合中最后1个元素,或返回集合中满足指定条件的最后个元素,后者在不存在满足条件的元素时返回默认元素。
Single,SingleOrDefault
都返回集合中唯一元素,或返回集合中满足指定条件的唯一元素,后者在不存在满足条件的元素时返回默认元素,该集合必须
有且只有一个元素,晕倒!!!
protected void Button11_Click(object sender, EventArgs e)
{
int[] ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var fast1=ints.First();
//用Lambda表达式来指定条件
var fast2 = ints.First(u => u > 6);
var last1 = ints.Last();
var last2 = ints.Last(u => u < 6);
Label11.Text = fast1.ToString() + "|" + fast2.ToString() + "|" + last1.ToString() + "|" + last2.ToString();
}
7.数据类型转换操作(大部分都是根据原数据源做相应类型转换后返回一个相应的IEnumerable<T>类型的新序列)
AsEnumerable
可将数据源转换为IEnumerable<T>类型的序列,但该操作只能在实现了IEnumerable<T>的数据源上运行。
Cast<T>
可将数据源里的元素转换为T类型
OfType<T>
与Cast<T>不同,其是筛选数据源中元素类型是T类型的元素
protected void Button12_Click(object sender, EventArgs e)
{
int[] ints = new int[] { 1, 2, 3, 4 };
var AsEnumerable = ints.AsEnumerable();
var Cast = ints.Cast<string>();
int sum = int.Parse(Cast.ElementAt(1)) + int.Parse(Cast.ElementAt(2));
var OfType = ints.OfType<string>();
foreach (var item in AsEnumerable)
{
Label12.Text += item.ToString();
}
if (OfType.Count() == 0) //此处不能是OfType==null,OfType实质上不为null
{
Label12.Text+="|"+"由于ints里没有string类型的元素所以没有筛选到任何结果"+"|";
}
Label12.Text += sum.ToString();
}
AsQueryable
可将数据源转换为IQueryable<T>类型或IQueryable类型的序列,只能在实现了IEnumerable<T>的数据源上运行,否则在运行时会出错。
ToList,ToArray
ToList将数据源转换为List<T>类型的序列,ToArray将数据源转换为T[]数组,只能在实现了IEnumerable<T>的数据源上运行。前面也有用过ToArray,是在List<T>序列上运行的,因为List<T>实现了IEnumerable<T>。
ToDictionary
将数据源中的元素按照键值放入一对一的字典序列(Dictionary<键类型,值类型>)中。
http://www.cnblogs.com/yjmyzz/archive/2009/12/04/1617240.html
http://book.51cto.com/art/200906/130322.htm
protected void Button14_Click(object sender, EventArgs e)
{
List<Person> list = new List<Person>();
list.Add(new Person{ Id=1, Name="刘海", Age=35});
list.Add(new Person{ Id=2,Name="中分",Age=53});
list.Add(new Person { Id = 3, Name = "三七分", Age = 88 });
Dictionary<int, string> dic = list.ToDictionary(u=>u.Id,u=>u.Name);
//指定键的类型和值的类型 指定 键 和 值
foreach (var item in dic)
//item类型为KeyValuePair<Tkey,Tvalue>(键值对),每个元素都是一个键/值对,既不是键的类型,也不是值的类型
{
Label14.Text +=item.Key.ToString()+item.Value; //遍历字典序列里的键与值
}
Label14.Text += "|" + dic[1]; //根据键取值
}
ToLookup
与ToDictionary类似,不过其是将数据源的元素按照键值放入一对多的字典序列(Lookup<键类型,值类型>)中,也就是说一个键可以对应多个值。
http://book.51cto.com/art/200906/130323.htm
protected void Button15_Click(object sender, EventArgs e)
{
List<Person> list = new List<Person>();
list.Add(new Person { Id = 1, Name = "刘海", Age = 35 });
list.Add(new Person { Id = 1, Name = "中分", Age = 53 });
list.Add(new Person { Id = 1, Name = "三七分", Age = 88 });
ILookup<int, Person> loo =list.ToLookup(u => u.Id, u => u);
//另一种写法Lookup<int, Person> loo =(Lookup<int, Person>)list.ToLookup(u => u.Id, u => u);
foreach (var items in loo)
//与Dictionary<int, string>不同,ILookup<int, Person>里的元素items是IGrouping<键类型,值类型>,因此还 要遍 历一次
{
foreach (var item in items)
//item的类型是值类型,这里是Person类型
{
Label15.Text += item.Name;
}
}
}
8.生成操作
DefaultIfEmpty,Empty,Range,Repeat(直接上代码)
protected void Button16_Click(object sender, EventArgs e)
{
List<int> list1 = new List<int>() { 1, 2, 3, 4, 5 };
List<int> list2 = new List<int>();
var DefaultIfEmpty1 = list1.DefaultIfEmpty(1);
var DefaultIfEmpty2 = list2.DefaultIfEmpty(1); //如果序列为空返回元素值为默认值或设置的一个值的
IEnumerable<T>类型的序列
Label16.Text += "在不为空的序列上运行DefaultIfEmpty():";
foreach (var item in DefaultIfEmpty1)
{
Label16.Text+= item.ToString();
}
foreach (var item in DefaultIfEmpty2)
{
Label16.Text +="在为空的序列上运行DefaultIfEmpty():"+ item.ToString();
}
var Empty = Enumerable.Empty<string>(); //返回类型为IEnumerable<指定类型>的空序列
var Range = Enumerable.Range(3, 5); //返回类型为IEnumerable<int>的序列,参数为开始数字和数字个数
var Repeat = Enumerable.Repeat<string>("哇!", 5); //返回类型为IEnumerable<指定类型>的序列,参数为 重复的值和重复次数
//Empty,Range和Repeat操作只用于Enumerable类,而不能运行与任何序列上,不知有何用?
foreach (var item in Range)
{
Label16.Text += "Range:" + item.ToString();
}
foreach (var item in Repeat)
{
Label16.Text += "Repeat:" + item;
}
}
9.限定符操作
All,Any,Contains(分别检测序列中是否所有元素都满足条件,是否有元素满足条件,是否有指定元素,返回值为true和false)
protected void Button17_Click(object sender, EventArgs e)
{
List<int> list = new List<int>() {2,4,6,8,10};
bool All = list.All(u => u % 2 == 0);
bool Any = list.Any(u => u == 10);
bool Contains = list.Contains(3);
Label17.Text = "所有元素都是偶数:" + All.ToString() + "有元素等于10:" + Any.ToString() + "是否有 元素3" + Contains.ToString();
}
10.数据分区操作
Skip,Take,SkipWhile,TakeWhile
protected void Button18_Click(object sender, EventArgs e)
{
int[] ints = new int[] {1,2,3,4,5,6,7,8,9,10};
var Skip = ints.Skip(9);//从开头跳过序列中指定数量的元素,返回剩余的元素的序列
var Take = ints.Take(9);//从开头提取序列中指定数量的元素,返回它们的序列
var SkipWhile = ints.SkipWhile(u => u < 6);//跳过满足条件的元素,返回剩余元素的序列
var TakeWhile = ints.TakeWhile(u => u < 6);//提取满足条件的元素,返回它们的序列
foreach (var item in Skip)
{
Label18.Text += item.ToString()+"|";
}
foreach (var item in Take)
{
Label18.Text += item.ToString();
}
Label18.Text += "|";
foreach (var item in SkipWhile)
{
Label18.Text += item.ToString();
}
Label18.Text += "|";
foreach (var item in TakeWhile)
{
Label18.Text += item.ToString();
}
}
11.联接操作
JOIN,GroupJoin
http://www.cnblogs.com/sunnycoder/archive/2010/01/17/1649831.html
http://www.cnblogs.com/stevenxie/archive/2009/08/23/1552521.html
http://www.cnblogs.com/zxjay/archive/2008/09/05/1285418.html
前面已经学习了join查询表达式,我们也可用join查询操作来实现同样效果,关于join前面提到过类似于sql里的innerjoin,查
询到的结果集是两个数据源某一方面相同的交集或交集中元素的任何属性
protected void Button19_Click(object sender, EventArgs e)
{
List<int> list1=new List<int>(){1,2,3,4,5,6};
List<int> list2 = new List<int>() { 2, 4, 6, 8, 10 };
var Join1 = list1.Join(list2, u => u, v => v,(u,v)=>u);
//join查询操作:数据源1.Join(数据源2,数据源1的某方面,数据源2的某方面,返回该方面相同的交集),运行时会自动检索
俩数据源的某方面的相同处
var Join2=from u in list1 join r in list2 on u equals r select u; //join查询表达式
foreach (var item in Join1)
{
Label19.Text += item;
}
Label19.Text += "|";
foreach (var item in Join2)
{
Label19.Text += item.ToString();
}
}
类似于sql里的外连接,前面讲的jion查询语句通过后面加上into来通过分组联接实现,同样GroupJoin也能实现,实现效果是将数据源1里的元素或元素的任何属性全部输出,数据源2里的则输出那些在某方面与数据源1中的元素有相同处的元素或元素的属性,即比join查询操作多输出了一些数据源1里的元素
protected void Button20_Click(object sender, EventArgs e)
{
List<string> list1 = new List<string>() {"我","是","个","好","人"};
List<string> list2 = new List<string>() {"我","是","个","坏","人"};
var GroupJoin1 = list1.GroupJoin(list2, u => u, v => v, (u,v) =>v);
//这里v代表"数据源2"里满足条件的元素的集合,类型本身是IEnumerable<T>再返回后GroupJoin1的类型成了
IEnumerable<IEnumerable<T>>了,而u代表"数据源1"里的元素的集合,其类型是T
var GroupJoin2 = from u in list1 join v in list2 on u equals v into g select u;//注意是u不是g
同样u的类型是T,g的类型是IEnumerable<T>
foreach (var items in GroupJoin1) //需两层foreach
{
foreach (var item in items)
{
Label20.Text += item;
}
}
foreach (var item in GroupJoin2) //只需一层foreach因为前面是select u不是select g(g代表"数据源2"里
满足条件的元素的集合)
{
Label20.Text += item;
}
}
注意:无论是分组联接(看前面的分组联接)还是GroupJoin,如果要遍历"数据源2"里满足条件的元素需两层foreach语句,因为无论是"v"还是"g"它们的类型都是IEnumerable<T>。
12.相等操作
SequenceEqual
用于判断俩序列是否相同,首先判断俩序列是否相同要看它们的元素数量及对应元素是否都相等
protected void Button21_Click(object sender, EventArgs e)
{
int[] ints1 = new int[] { 1, 2 };
int[] ints2 = new int[] { 1, 3 };
var SequenceEqual = ints1.SequenceEqual(ints2);
Label21.Text = "俩序列是否相等:" + SequenceEqual.ToString();
}
13.串联操作
Concat
用于将俩序列的元素串联起来,然后返回一个新序列,当然这俩序列里元素类型要一样
protected void Button22_Click(object sender, EventArgs e)
{
int[] ints1 = new int[] { 1, 2 };
int[] ints2 = new int[] { 1, 3 };
var Conact = ints1.Concat(ints2);
foreach (var item in Conact)
{
Label22.Text += item.ToString();
}
}