Linq教学
1 委托
1.1委托用法
委托是可以指向方法的类型,调用委托变量时执行的就是变量指向的方法
用法
delegate 返回类型 方法名(参数);
举例:
internal class Program
{
static void Main(string[] args)
{
/* string list = "12321312";
var items = list.Where(c => char.IsLetter(c));*/
int i = 5;
Console.WriteLine(i);
Console.WriteLine("-----------------");
D1 d1 = c1;
d1();
d1 = c2;
d1();
}
static void c1()
{
Console.WriteLine("这是c1方法");
}
static void c2()
{
Console.WriteLine("这是c2方法");
}
delegate void D1();
}
1.2委托类型
.NET中定义了泛型委托Action(无返回值)和Func(有返回值),所以一般不用自定义委托类型。
用法:
Func=> Func<参数1类型,参数2类型,返回值类型> f = 方法
Action=> Action<参数1类型,参数2类型> a= 方法
举例:
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("-----------------");
Func<int,int,int>f=Add;
Func<int,int,string>f2=f33;
Action<int,string> f6 =f8;
}
static int Add(int i,int j) {
return i1+i2
}
static string f33(int i,int j)
{
return "xxx"
}
static void f1()
{
Console.WriteLine("这是c2方法");
}
delegate void D1();
}
}
1.3 委托方法
委托可以指定匿名方法
用法:
Func<int,int,string> f1=delegate (int i ,int b){
return $"{i}+{b}}"
}
string s =f1(1,2)
//第二种用法
Func<int,int,string> f2=(int i ,int b)=>{return $"{i}+{b}}"}
Console.Write(f2(1,3));
2 Linq 方法的背后
- LINQ中提供了很多集合的拓展方法,配合lambda能简化数据处理。
- 代码
int[]nums =new int []{3,99,88,77,7,8,9,66,15,7};
IEnumerableitems=nums.Where(i=>i>10);
//using System.Linq
上述问题如何通过方法实现
- 实现方法:
internal class Program
{
static void Main(string[] args)
{
int[] nums = new int[] { 3, 99, 88, 77, 7, 8, 9, 66, 15, 7 };
var list = nums.Where(p => p > 10);
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.WriteLine("-------where1---------");
var items2 = myWhere1(nums, a => a > 10);
foreach (var item in items2)
{
Console.WriteLine(item);
}
Console.WriteLine("------where2----------");
var items3 = MyWhere(nums, a => a > 10);
foreach (var item in items3)
{
Console.WriteLine(item);
}
}
static IEnumerable myWhere1(IEnumerable nums, Func<int, bool> f)
{
List list = new List();
foreach (var item in nums)
{
if (f(item))
{
list.Add(item);
}
}
return list;
}
static IEnumerable MyWhere(IEnumerablenums, Func<int,bool> f)
{
foreach (var item in nums)
{
if (f(item) == true)
{
yield return item;
}
}
}
}
3 C#中var
var可以让编译器的“类型推断”来简化类型的声明,在LINQ中常用。
C#中的var和JavaScript的var不一样,仍然是强类型的。
C#中的弱类型是dynamic
4 linq中的拓展方法
LINQ中提供了大量类似Where的拓展方法,简化数据处理。大部分都在System.Linq命名空间中。
4.1 wh
- 添加类
record 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; }
}
- 添加基础数据
list.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salary = 5000 });
list.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salary = 3000 });
list.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salary = 9000 });
list.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salary = 2000 });
list.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salary = 1000 });
list.Add(new Employee { Id = 6, Name = "nancy", Age = 35, Gender = false, Salary = 8000 });
list.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 });
list.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salary = 8000 });
4.2Where
通过where进行筛选
IEnumerable list1 = list.Where(e => e.Salary > 2500 && e.Age < 35);
foreach (Employee e in list1)
{
Console.WriteLine(e);
}
4.3Count
可以计算满足条件的所有数量
int count1 = list.Count(e => e.Salary > 5000 || e.Age < 30);
int count2 = list.Where(e => e.Salary > 5000 || e.Age < 30).Count();
4.4 Any
只要有一条满足的情况下则为true
bool b1 = list.Any(e => e.Salary > 8000);
bool b2 = list.Where(e => e.Salary > 8000).Any();
4.5返回一条数据的方法
- Single:有且只有一条满足条件的数据,超过一条或不存在则报错
- SingleOrDefault:最多只有一条满足要求的数据,不存在则返回默认值,超过则报错
- First:至少有一条,返回一条;不存在报错
- FirstOrdefault:返回第一条或者默认值;不存在返回默认值
Employee e1 = list.Single(e => e.Id == 6);
Console.WriteLine(e1);
Employee? e2 = list.SingleOrDefault(e => e.Id == 9);
if (e2 == null)
{
Console.WriteLine("没有Id==9的数据");
}
else
{
Console.WriteLine(e2);
}
Employee e3 = list.First(e => e.Age > 30);
Console.WriteLine(e3);
Employee? e4 = list.FirstOrDefault(e => e.Age > 30);
if (e4 == null)
{
Console.WriteLine("没有大于30岁的数据");
}
else
{
Console.WriteLine(e2);
}
Employee e5 = list.First(e => e.Salary > 9999);
4.6 Order排序
-
Order()对数据正序排序;
-
OrderByDescending()倒序排序;
-
list.OrderBy(e=>e.Age); 通过年龄排序
对于简单类型排序,也许不用lambda表达式。
-
特殊案例 按照最后一个字符排序;用Guid或者随机数进行排序。
Console.WriteLine("------按照年龄正序排列------");
var orderedItems1 = list.OrderBy(e => e.Age);
foreach (var item in orderedItems1)
{
Console.WriteLine(item);
}
Console.WriteLine("------按照工资倒序排列------");
var orderedItems2 = list.OrderByDescending(e => e.Salary);
foreach (var item in orderedItems2)
{
Console.WriteLine(item);
}
-----------------------------
//通过GUid排序
var item2=list.orderBydescending(e=>Guid.NewGuid());
//随机数排序
var item3 =list.OrderByDescending(e=>rand.Next())
-------------------------
//多条件排序
//通过年龄进行排序,年龄相同的通过工资从打道夏排序
var items=list.OrderByDescending(e=>e.Age).thenBy(e=>e.Salary);
4.7 Skip,take 限制结果集,获取部分数据
- skip(n) 跳过n条数据,Take(n)获取n条数据
- 案例:获取从第二条开始获取3条数据
var orderedItems1 =list.Skip(2).Take(3);
// Skip(),Take()也可以单独使用
4.8 聚合函数
-
Max() 取最大
-
Min() 取最小
-
Average() 平均值
-
Sum() 求和
-
Count() 求数量
LINQ中所有的拓展方法都是针对IEnumerable接口的,而几乎所有能返回集合的都返回IEnumerable,所以是可以吧几乎所有的方法“链式使用”的
//查询出年龄大于30的,工资最小的
list.Where(e=>e.Age>30).Min(e=>e.Salary)
4.9分组 GroupBy
- GroupBy()方法参数是分组条件表达式,返回值为IGrouping<TKey,TSource>类型的泛型IEnumerable,也就是每一组以一个Grouping对象的形式返回,IGrouping 是一个继承自IEnumerable的接口,IGrouping中Key属性表示这一个分组数据的值。
- 例子:根据年龄分组,获取每组人数,最高工资,平均工资。
IEnumerable> items = list.GroupBy(e => e.Age);
foreach (IGrouping item in items)
{
int age = item.Key;
int count = item.Count();
int maxSalary = item.Max(e => e.Salary);
double avgSalary = item.Average(e => e.Salary);
Console.WriteLine($"年龄{item.Key},人数{count},最高工资{maxSalary},平均工资{avgSalary}");
}
4.10 投影
- 把集合中的每一项转换成另外的一种类型。
IEnumerable ages=list.Select(e=>e.Age);
IEnumerable names=list.Select(e=>e.Gender?"男":"女");
var dogs =list.Select(p=>new Dog{NickName=e.Name,Age=e.Age});
- 投影与匿名函数相互
var list2= list.Select(e => new { Xingming = e.Name, Xingbie = e.Gender ? "男" : "女" });
foreach(var i in list2)
{
Console.WriteLine("姓名:" + i.Xingming + ",性别:" + i.Xingbie);
}
- 练习
//通过年龄进行分组 求出这个年龄下的最高薪资和最低薪资以及这个年龄总共有多少人
var items1 =list.GroupBy(p=>p.Age).Select(p=> new {Nianling=p.Key,MaxS=p.Max(e=>e.Salary),MinS=p.Min(e=>e.Salary),Renshu=p.Count()});
foreach(var e in items1)
{
Console.WriteLine(e.Nianling+","+e.MaxS+","+e.MinS+","+e.Renshu);
}
4.11 集合的转换
-
有一些地方需要数组类型或者List类型的变量,我们可以用ToArray()和ToList()分别把IEnumerable< T > 转换为数组类型和List< T >类型。
- 操作
IEnumerable items2 = list.Where(p => p.Salary>6000);
List list3 = items2.ToList();
Employee[] array2 = items2.ToArray();
4.12 Linq查询语法
-
上述使用 Where,OrderBy ,Select等拓展该方法进行数据查询的语法叫做“LINQ方法语法”
,还有一种“查询语法”的写法
比如
var items3 = from e in list
where e.Salary > 3000
orderby e.Age
select new
{
e.Name,
e.Age,
Gender = e.Gender ? "男":"女"
};
5 面试中可能遇见的问题
- 问题1 有一个用逗号分割的表示成绩的字符串如"61,90,100,99,18,22,38,66,80,93,55,50,89"计算这些成绩的平均值
string txt= "61,90,100,99,18,22,38,66,80,93,55,50,89";
//先通过,进行切割
var items = txt.Split(",");
//需要将其转换成 int 类型然后进行求平均值
double avg = items.Select(e=>Convert.ToInt32(e)).Average();
Console.WriteLine(avg);
- 问题2 统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出频率高于2次的单词和其出现的的频率
//思路:首先先要去除空格和逗号,在将其转换成小写,再将其分组 获取次数 在进行排序
string s = "Hellow WORD,hhhhh,LaAALSDSADSs";
var items2 = s.Where(c => char.IsLetter(c)).Select(c => char.ToLower(c))
.GroupBy(c => c)
.Select(g => new { g.Key, Count = g.Count() })
.OrderByDescending(g => g.Count)
.Where(g=>g.Count>2);
foreach(var i in items2)
{
Console.WriteLine(i);
}