P22:为啥要学LINQ

 

 对比Python :numpy

学习路线

 

用数据类型定义的理念解释委托 

委托:方法的(数据)类型,指向方法的类型,对比 int i=5;整数类型的i指向整数5;数据的类型

 

 

D1 d = F1;//注意F1不带括号(),带了表示方法调用了
d();//执行D1所指向的方法
d = F2;
d();

D2 d2 = Add;
Console.WriteLine(d2(3, 5));
Console.ReadLine();


static void F1()
{
    Console.WriteLine("我是F1");
}
static void F2()
{
    Console.WriteLine("我是F2");
}
static int Add(int i1,int i2)
{
    return i1 + i2;
}

delegate void D1();//委托要定义在后面
delegate int D2(int a,int b);//参数名字可以不一样,但是类型必须一样

 

 日常编程中我们几乎不会自己定义委托,C#已经内置了两个泛型委托

 

 

 

 最多16个,所以基本够用了

Func<int, int, int> f = Add;//最后一个是返回值类型,类型根据指定的函数来确定
Console.WriteLine(f(3, 5));

Action action = F1;
action();
Console.ReadLine();

效果一致

 P23:Lambda是怎么来的

 

 匿名方法:没有名字的方法

Action f1 = delegate ()//匿名方法,无参数无返回值
{
    Console.WriteLine("我是aaa");
};
f1 ();
Action<int, int> f2 = delegate (int a, int b)//匿名方法,有参数无返回值
{
    Console.WriteLine(a + b);
};
f2(3, 4);

Func<int, int, int> f3 = delegate (int a, int b)//匿名方法,有参数有返回值
{
    return a * b;
};
Console.WriteLine(f3(2, 5));


Console.ReadLine();

 

 => goes to

去掉delegate,参数列表后面、{前面添加=>符号,参数不再需要指明类型(当然可以指明类型,要指明都指明,不能只指明一部分参数的类型)

Func<int, int, int> f4 = (a, b) =>//匿名方法,Lambda表达式写法
{
    return a * b;
};
Console.WriteLine(f3(3, 5));
Action f5 = () => Console.WriteLine("无返回值,且只有一条语句,可以连{}也省略");
f5();

 

 

 

 

Func<int, int, int> f42 = (a, b) => a * b;
Console.WriteLine(f42(3, 5));

 

 

Action<int> f6 = a => Console.WriteLine(a + "只有一个参数");
f6(5);
Func<int, bool> f66 = a => a > 0;
Console.WriteLine( f66(55));

 

P24:揭秘LINQ方法的背后(自己写一个)

 

 

int[] num=new int[] {5,8,22,2,5,77,8,552,2,255,412,9};
//Where方法会遍历集合中每个元素,对于每个元素
//都调用a=>a>10这个表达式判断一下是否为true
//如果为true,则把这个放到返回的集合中
IEnumerable<int> result=num.Where(x => x > 100);//IEnumerable的扩展方法,需要引用LINQ
foreach (var x in result)
{
    Console.WriteLine(x);
}

Console.WriteLine("--------------自己写一个-------------------");

//自己写一个
static IEnumerable<int> MyWhere(IEnumerable<int> items,Func<int,bool> f)
{
    List<int> result=new List<int>();
    foreach(int item in items)
    {
        if (f(item)) { 
            result.Add(item); 
        }
    }
    return result;
}

IEnumerable<int> ints = MyWhere(num, a => a > 100);
foreach (var x in ints)
{
    Console.WriteLine(x);
}


Console.ReadLine();

更牛的一种写法

 

Console.WriteLine("--------------更牛的写法yield-------------------");
//更牛的写法yield
static IEnumerable<int> MyWhere2(IEnumerable<int> items, Func<int, bool> f)
{

    foreach (int item in items)
    {
        if (f(item))
        {
            yield return item;//优点,流水线处理,边执行边返回,效率更高
        }
    }
}

IEnumerable<int> int2 = MyWhere(num, a => a > 100);
foreach (var x in int2)
{
    Console.WriteLine(x);
}

补充知识

 

 赋值后类型就确定了,不能再更改了,可以反编译看一下

后面会讲匿名类型+var才能真正发挥var的作用

 P25:常用扩展方法一

 

 IEnumerable<T>的扩展方法

实现了 IEnumerable接口的都可以使用LINQ扩展方法,如数组、List、Dictionary、Set.....

 

基础数据

    public 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}";
        }

    }
List<Employee> list=new List<Employee>();
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 =85000 });
list.Add(new Employee() { Id = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 });
list.Add(new Employee() { Id = 8, Name = "djd", Age = 33, Gender = true, Salary = 8000 });

Where方法

 

 

IEnumerable<Employee> employees = list.Where(x => x.Age > 30);
foreach(Employee employee in employees)
{
    Console.WriteLine(employee);
}
Console.ReadLine();

 

 注意区别List的count属性

Console.WriteLine(list.Count());//无参数
Console.WriteLine(list.Count(x=>x.Salary>5000));//有参数
Console.WriteLine(list.Count(x=>x.Salary>8000 && x.Age>20));//多条件参数
Console.ReadLine();

 

 

Console.WriteLine(list.Any(x => x.Age == 35));//至少有一条返回TRUE,可以用Count,但是Count要数完了才能返回,效率低
Console.WriteLine(list.Any(x => x.Age == 35000));

 

P26:常用扩展方法二

 

 

Single:必须取到有且只有一条,否则报错

SingleOrDefault:最多一条或者无返回数据,无返回数据时根据返回类型推断(可能是null或者int等等,看具体返回值类型),多条报错

First:取第一条,无数据报错

FirstOrDefault:取第一条,无数据返回默认值

//-----------------P26--------------------
Employee employee1;
//employee1= list.Single();
//Console.WriteLine(employee1);//:“Sequence contains more than one element”
//employee1 = list.Single(x=>x.Name== "djd");//和下一行一样
//employee1 = list.Where(x=>x.Name=="djd").Single();
employee1 = list.SingleOrDefault(x => x.Name == "djdd");//没有则返回null(根据实际情况返回)
//employee1 = list.SingleOrDefault(x => x.Gender == true);//存在多条报错
Console.WriteLine(employee1);

Employee employee2;
employee2 = list.First(e=>e.Age>30);
Console.WriteLine(employee2);
employee2 = list.FirstOrDefault(e=>e.Age > 300);//返回null
Console.WriteLine(employee2);

 

 Order排序,对之前的数据不受影响,返回的新的数据集合排序

排序不一定非得要用属性

IEnumerable<Employee> item=list.OrderBy(x=>x.Age);
foreach(Employee employee in item) { 
    Console.WriteLine(employee); 
}
int[] nums=new int[] {1,5,7,5,5,8,88,1,5,2,2,5,4,2,55};
IEnumerable<int> ints=nums.OrderBy(x=>x);//必须加一个参数,没有就加自己本身
foreach (int i in ints)
{
    Console.WriteLine(i);
}

//var item2=list.OrderBy(x=>Guid.NewGuid());//随机排序方法一
Random rand=new Random();//随机排序方法二
var item2 = list.OrderBy(x => rand.Next());
foreach (Employee employee in item2) {
    Console.WriteLine(employee);
}
Console.WriteLine("----------------------");
var item3 = list.OrderBy(x => x.Name[x.Name.Length-1]);//取姓名最后一个字母排序
foreach (Employee employee in item3)
{
    Console.WriteLine(employee);
}
Console.ReadLine();

 

 一个OrderBy只能对一个字段排序,多个字段排序不能使用两个OrderBy,ThenBy可以连续多个使用

item3 = list.OrderBy(x => x.Age).ThenBy(x=>x.Salary).ThenByDescending(x=>x.Name);//
foreach (Employee employee in item3)
{
    Console.WriteLine(employee);
}

 

 

var item4 =list.Where(e=>e.Age>30).OrderBy(e=>e.Name).Skip(2).Take(3);//链式操作,Skip\Take也可以单独使用
foreach (Employee employee in item4)
{
    Console.WriteLine(employee);
}

 

P27:常用扩展方法三

 

 

 

var itemMaxAge = list.Max(e => e.Age);//年龄最大值
Console.WriteLine(itemMaxAge);
var itemMaxSalary = list.Where(e=>e.Id>6).Max(e => e.Salary);//序号大于6的工资最大值
Console.WriteLine(itemMaxSalary);
var itemMaxName = list.Max(e => e.Name);//同样可以返回字符串的最大值
Console.WriteLine(itemMaxName);

var itemAvgSalary = list.Where(e => e.Age > 30).Average(e => e.Salary);//年龄大于30岁的平均工资,返回Double类型
Console.WriteLine(itemAvgSalary);

 

 

 

//IEnumerable<IGrouping<int, Employee>> groupings = list.GroupBy(e => e.Age).OrderBy(g => g.Key);//完整写法//IGrouping继承IEnumerable,所以可以先理解为一样的处理方式
var itemgroup =list.GroupBy(e => e.Age).OrderBy(g=>g.Key);
foreach (var group in itemgroup)
{
    Console.WriteLine($"------------{group.Key}--------------");
    Console.WriteLine($"最大工资:{group.Max(e=>e.Salary)}");
    foreach(Employee emp in group)
    {
        Console.WriteLine(emp);
    }
    Console.WriteLine("----------------------------");
}

运行结果

 补充一个多字段分组的代码写法案例

 

Console.WriteLine("---------------多字段分组----------------");
var itemgroupMult = list.GroupBy(e => new { e.Age, XB = e.Gender ? "" : "" }).OrderBy(g => g.Key.Age).ThenBy(g => g.Key.XB);//.OrderBy(g => g.Key);这样写语法不报错,运行报错
foreach (var group in itemgroupMult)
{
    Console.WriteLine($"------------{group.Key}--------------");
    Console.WriteLine($"最大工资:{group.Max(e => e.Salary)}");
    foreach (Employee emp in group)
    {
        Console.WriteLine(emp);
    }
    Console.WriteLine("----------------------------");
}

 

 

P28:常用扩展方法四

 

 Select就是投影操作

var item281 = list.Select(e => e.Age);//返回IEnumerable<int>集合
foreach(var emp in item281)
{
    Console.WriteLine(emp);
}
var item282 = list.Where(e=>e.Age>30).Select(e => e.Name+","+e.Age + "," + (e.Gender?"":""));//返回IEnumerable<string>集合
foreach (var emp in item282)
{
    Console.WriteLine(emp);
}

Console.WriteLine("---------------投影为Dog类型----------------");
var item283=list.Select(e => new Dog { NickName=e.Name,Age= e.Age });
foreach (var emp in item283)
{
    Console.WriteLine(emp.NickName+","+emp.Age);
}

 

 

//匿名类型,只能使用var定义
//反编译后会发现,编译器随机起了一个类名
var obj1 = new { Name = "ddd", Age = 28, Salary = 20000 };
Console.WriteLine(obj1.Name + "," + obj1.Age + "," + obj1.Salary + ",");

 

 

Console.WriteLine("---------------投影为匿名类型----------------");
var item284 =list.Select(list => new { NickName=list.Name,Age= list.Age,XingBie=list.Gender?"":"" });//这里只能使用var获取返回值,由编译器推断
foreach(var emp in item284)//var推断
{
    Console.WriteLine(emp.NickName + "," + emp.Age + "," + emp.XingBie + ",");
}

Console.WriteLine("---------------投影为匿名类型复杂应用----------------");
var item285 = list.GroupBy(e => e.Age).Select(g => new { g.Key,AvgSalart= g.Average(e => e.Salary),MaxSalary=g.Max(e=>e.Salary) });
foreach (var emp in item285)
{
    Console.WriteLine(emp.Key + "," + emp.AvgSalart + "," + emp.MaxSalary + ",");
}

P29:链式调用

IEnumerable<Employee> item291 = list.Where(e => e.Salary > 5000);
List<Employee> employees1 = item291.ToList();//转为LIst类型
Employee[] employees2 = employees1.ToArray();//转为数组类型

 

 

Console.WriteLine("---------------链式调用小考----------------");
var item292 =list.Where(e=>e.Id>2).GroupBy(e=>e.Age)//使用GroupBy以后的链接操作都是针对Group的
    .OrderBy(g=>g.Key)
    .Take(3)
    .Select(g=>new {NL=g.Key,RS=g.Count(),PJGZ=g.Average(e=>e.Salary)});//注意g、e各自代表的数据范围
foreach(var emp in item292)
{
    Console.WriteLine(emp.NL + "," + emp.RS + "," + emp.PJGZ);
}

P30:LINQ另一种倩影

 可以不用管,只是写法不一样罢了,看到能明白就行

 

 写LINQ更像是写SQL语句

Console.WriteLine("---------------类似于SQL的一种写法----------------");
Console.WriteLine("---------------编译后代码一样,看懂就行----------------");

var item293 = from e in list
          where e.Salary > 500
          select new { e.Age, e.Name, XB = e.Gender ? "" : "" };
foreach (var emp in item293)
{
    Console.WriteLine(emp.Age + "," + emp.Name + "," + emp.XB);
}

 

 P31:LINQ解决面试问题

 

 三个数取最大值

Console.WriteLine("---------------三个数取最大值----------------");
int ii = 5;
int ij = 6;
int ik= 7;
int[] ints1= new int[] { ii,ij,ik};
Console.WriteLine(ints1.Max(e => e));//LINQ方法
Console.WriteLine(Math.Max(ii, Math.Max(ij, ik)));//基础方法

 

 

Console.WriteLine("---------------案例一----------------");

string s = "61,90,100,99,22,3,5,333,2,3,3,2,3";
string[] ints2 = s.Split(",");
var p311= ints2.Select(e=>int.Parse(e));
Console.WriteLine(p311.Average(e=>e));//分布实现
Console.WriteLine(s.Split(",").Select(e => int.Parse(e)).Average(e=>e));//一步实现

 

 

Console.WriteLine("---------------案例二----------------");

string s2 = "Hello,world,HHLIk;";//string实现了IEnumerable<char>
//去掉Where筛选发现排序失败了
//var x311 = s2.Where(c => char.IsLetter(c))
//    .Select(c => char.ToLower(c))
//    .GroupBy(e => e)
//    .OrderByDescending(g => g.Key)
//    .Where(g => g.Count() > 2)
//    .Select(g => new { g.Key, PL = g.Count() });

//foreach (var x in x311)
//{
//    Console.WriteLine(x);
//}
Console.WriteLine("-------------------------------");

var x311 = s2.Where(c => char.IsLetter(c))
   .Select(c => char.ToLower(c))
   .GroupBy(e => e)
   .Select(g => new { g.Key, PL = g.Count() })
   .OrderByDescending(g => g.PL)
   .Where(g => g.PL > 2);

foreach (var x in x311)
{
    Console.WriteLine(x);
}