什么是LINQ
LINQ(读音link)代表语言集成查询(Language Integrated Query),是.NEt框架的扩展,它允许我们用SQL查询数据库的方式来查询数据的集合,使用它,你可以从数据库、程序对象的集合以及XML文档中查询数据
复习委托:
委托是可以指向方法的类型,调用委托变量时执行的就是变量指向的方法。
using System;
using System.ComponentModel.Design.Serialization;
using System.Runtime.CompilerServices;
namespace ConsoleApp1
// Note: actual namespace depends on the project name.
{
class Program
{
static void Main(string[] args)
{
D1 d = F1;
d();
d = F2;
d();
D2 d2 = Add;
Console.WriteLine(d2(3,5));
}
static void F1()
{
Console.WriteLine(" i am f1");
}
static void F2()
{
Console.WriteLine(" i am f2");
}
static int Add(int x,int y)
{
return x + y;
}
}
delegate void D1();
delegate int D2(int i,int j);
}
.Net中定义了泛型委托Action(无返回值)和Func(有返回值),所以一般不用自定义委托类型。
委托变量不仅可以指向普通方法,还可以指向匿名方法。
using System;
using System.ComponentModel.Design.Serialization;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
namespace ConsoleApp1
// Note: actual namespace depends on the project name.
{
class Program
{
static void Main(string[] args)
{
Action f1 = delegate ()
{
Console.WriteLine(" ia am rachel");
};
f1();
Action<string, int> f2 = delegate (string n, int i)
{
Console.WriteLine($"n={n},i={i}");
};
f2("rachel", 18);
Func<int, int, int> f3 = delegate (int i, int j)
{
return i + j;
};
Console.WriteLine(f3(3, 5));
}
}
}
匿名方法可以写成lambda表达式,可以省略参数数据类型,因为编译能根据委托类型推断出参数类型,用=>引出来方法体
Func<int, int, int> f3 = delegate (int i, int j)
{
return i + j;
};
Console.WriteLine(f3(3, 5));
Func<int, int, int> f4 = (int i, int j)=>
{
return i + j;
};
Console.WriteLine(f4(5, 8));
如果委托没有返回值,且方法体只有一行代码,那么{}可以省略
如果=>之后的方法体中只有一行代码,且方法有返回值,那么可以省略方法体的{}以及return.
Func<int, int, int> f5 = ( i, j) =>
{
return i + j;
};
Console.WriteLine(f5(5, 8));
Func<int, int, int> f6 = (i, j) => i + j;
Console.WriteLine(f6(5, 8));
LINQ提供了很多集合的扩展方法,配合lambda能简化数据处理
using System;
using System.Linq;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 2, 3, 54, 21, 55, 698 };
//Where方法会遍历集合中每个元素,对于每个元素都调用a=>a>10这个表达式
//判断一下是否为true,如果都为true,则把这个放到返回的集合中
//
IEnumerable<int> result = nums.Where(a => a > 10);
foreach(int i in result)
{
Console.WriteLine(i);
}
}
static IEnumerable<int> MyWhere1(IEnumerable<int> items,Func<int,bool> f)
{
List<int> result=new List<int>();
foreach(int i in items)
{
if(f(i)==true)
{
result.Add(i);
}
}
return result;
}
static IEnumerable<int> MyWhere2(IEnumerable<int> items, Func<int, bool> f)
{
foreach (int i in items)
{
if (f(i) == true)
{
yield return i;
}
}
}
}
}
可以使用var让编辑器的“类型推断” ,来简化类型的声明。在LINQ中常用。
C#的var和JavaScript中的var不一样,仍然是强类型的,C#中的弱类型是dynamic。
LINQ中提供了大量类似Where的扩展方法,简化数据处理,大部分都在System.Linq命名空间中。注意,这些扩展方法都是基于IEumnberable<T>泛型扩展方法。
准备初始数据
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Employee
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public int Salary { get; set; }
public override string ToString()
{
return $"Id={Id},Name={Name},Age={Age},Gender{Gender},Salary={Salary}";
}
}
}
using System;
using System.Linq;
using System.Net.Cache;
using System.Reflection;
using System.Xml.Linq;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "rachel", Age = 34, Gender = true, Salary = 3652 });
list.Add(new Employee { Id = 2, Name = "alan", Age = 28, Gender = true, Salary = 8754 });
list.Add(new Employee { Id = 3, Name = "daniel", Age = 12, Gender = true, Salary = 3650 });
list.Add(new Employee { Id = 4, Name = "ben", Age = 22, Gender = false, Salary = 8955 });
list.Add(new Employee { Id = 5, Name = "normal", Age = 18, Gender = true, Salary = 5000 });
list.Add(new Employee { Id = 6, Name = "crush", Age = 28, Gender = true, Salary = 2544 });
list.Add(new Employee { Id = 7, Name = "cornor", Age = 36, Gender = true, Salary = 6000 });
list.Add(new Employee { Id = 8, Name = "zane", Age = 55, Gender = false, Salary = 5000 });
}
}
}
Where方法:每一项数据都会经过predicate的测试,如果针对一个元素,predicate执行的返回值为true,那么这个元素就会放到返回值中。
where参数是一个lambda表达式格式的匿名方法,方法的参数e表示当前判断的元素对象。参数的名字不一定非要叫e,不过一般lambda表达式中的变量名长度都不长.
using System;
using System.Linq;
using System.Net.Cache;
using System.Reflection;
using System.Xml.Linq;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "rachel", Age = 34, Gender = true, Salary = 3652 });
list.Add(new Employee { Id = 2, Name = "alan", Age = 28, Gender = true, Salary = 8754 });
list.Add(new Employee { Id = 3, Name = "daniel", Age = 12, Gender = true, Salary = 3650 });
list.Add(new Employee { Id = 4, Name = "ben", Age = 22, Gender = false, Salary = 8955 });
list.Add(new Employee { Id = 5, Name = "normal", Age = 18, Gender = true, Salary = 5000 });
list.Add(new Employee { Id = 6, Name = "crush", Age = 28, Gender = true, Salary = 2544 });
list.Add(new Employee { Id = 7, Name = "cornor", Age = 36, Gender = true, Salary = 6000 });
list.Add(new Employee { Id = 8, Name = "zane", Age = 55, Gender = false, Salary = 5000 });
IEnumerable<Employee> items=list.Where(e => e.Age > 30);
foreach(Employee e in items){
Console.WriteLine(e);
}
}
}
}
Count()方法:获取数据条数
Console.WriteLine(list.Count());
Console.WriteLine(list.Count(e => e.Age > 30));
Console.WriteLine(list.Count(e => e.Age > 30&&e.Salary>6000));
Any()方法:是否至少有一条数据满足要求
Console.WriteLine(list.Any(e=>e.Salary < 6000));
获取一条数据(是否带参数的两种写法):
Single:有且只有一条满足要求的数据;
SingleOrDefault:最多只有一条满足要求的数据;
First:至少有一条,返回第一条
FirstOrDefault:返回第一条或者默认值;
IEnumerable<Employee> items=list.Where(e => e.Name == "Jerry");
Employee e1 = items.Single();
// list.Where(e => e.Name == "Jerry").Single();
Console.WriteLine(e1);
Employee e2 = list.Where(e => e.Name == "Jerry").Single();
Console.WriteLine(e2);
选择合适的方法,“防御性编程”。
排序-Order()对数据进行正序排序
OrderByDescending()倒序排序;
对于简单类型排序,也许不用lambda表达式。特殊案例:按照最后一个字符排序;用Guid或者随机数进行随机排序。
IEnumerable<Employee> items2 = list.OrderBy(e => e.Salary);
foreach(Employee e in items2)
{
Console.WriteLine(e);
}
IEnumerable<Employee> items2 = list.OrderByDescenting(e => e.Salary);
foreach(Employee e in items2)
{
Console.WriteLine(e);
}
int[] nums = new int[] { 3, 9, 8, 10, 7, 6 };
IEnumerable<int> nums2 = nums.OrderBy(i => i);
foreach(var i in nums)
{
Console.WriteLine(i);
}
var items3=list.OrderBy(e=>e.Age).ThenByDescending(e=>e.Salary);
foreach(var e in items3)
{
Console.WriteLine(e);
}
限制结果集,获取部分数据:
Skip(n)跳过n条数据,Take(n)获取n条数据。
案例:获取从第2条开始获取3条数据
//跳过3条数据取出2条数据
var items4 = list.Skip(3).Take(2);
foreach (var e in items4)
{
Console.WriteLine(e);
}
var items5 = list.Where(e=>e.Age>30).OrderBy(e=>e.Age).Skip(1).Take(2);
foreach (var e in items5)
{
Console.WriteLine(e);
}
聚合函数:
Max(),Min(),Average(),Sum(),Count()
LINQ中多有得扩展方法几乎都是针对IEnumberable接口的,而几乎所有能返回集合的都返回IEumerable,所以是可以把几乎所有方法“链式使用”的。
分组:
GroupBy()方法参数是分组条件表达式,返回值为<IGroupingTKey,TSource>类型的泛型IEnumerable,也就是每一组以一个IGrouping对象的形式返回。IGrouping是一个继承自IEnumerable的接口,IGrouping中Key属性表示这一组的分组数据的值。
案例:根据年龄分组,获取每组人数,最高工资,平均工资。用var简化编程。
投影:
把集合中的每一项转换为另外一种类型,按照规则
IEnumerable<int> items6=list.Select(e=>e.Age);
foreach(int i in items6)
{
Console.WriteLine(i);
}
IEnumerable<string> items7 = list.Select(e => e.Name);
foreach (string i in items7)
{
Console.WriteLine(i);
}
IEnumerable<string> items8 = list.Where(e=>e.Age>30).Select(e => e.Name+","+e.Age);
foreach (string i in items8)
{
Console.WriteLine(i);
}
IEnumerable<string> items9 = list.Where(e => e.Salary > 4000).Select(e => e.Gender?"男":"女");
foreach (string i in items9)
{
Console.WriteLine(i);
}
IEnumerable<Dog> items10 = list.Select(e =>new Dog{Nickname=e.Name,Age=e.Salary});
foreach (Dog d in items10)
{
Console.WriteLine($"{d.NickName},{d.Age}");
}
var items11 = list.GroupBy(e => e.Age).Select(g => new { NianLing=g.Key,MaxS=g.Max(e=>e.Salary),MinS=g.Min(e1=>e1.Salary),Renshu=g.Count()});
foreach (var e in items11)
{
Console.WriteLine(e.NianLing+","+e.MaxS+","+e.MinS+","+e.RenShu);
}
集合转换:
有一些地方需要数组类型或者List类型的变量,我们可以用ToArray()方法和ToList()分别把IEnumerable<T>转换为数组类型和List<T>类型。
IEnumerable<Employee> item1 = list.Where(e => e.Salary > 6000);
List<Employee> list2=item1.ToList();
Employee[] array2=item1.ToArray();
链式调用:
Where,Select,OrderBy,GroupBy,Take,Skip等返回值都是IEnumerable<T>类型,所以可以链式调用,案例:
获取Id>2的数据,然后按照Age分组,并且把分组按照Age排序,然后取出前3条,最后再投影取得年龄,人数,平均工资。
// 获取Id>2的数据,然后按照Age分组,并且把分组按照Age排序,然后取出前3条,最后再投影取得年龄,人数,平均工资。
var items= list.Where(e => e.Id > 2).GroupBy(g => g.Age).OrderBy(g => g.Key).Take(3).Select(g => new { NL = g.Key, RS = g.Count(), PJ = g.Average(e => e.Salary) });
foreach(var i in items)
{
Console.WriteLine(i.NL+","+i.RS+","+i.PJ);
}