02020209 .NET Core重难点知识09-聚合函数、分组、投影、匿名类型、投影与匿名类型
02020209 .NET Core重难点知识09-聚合函数、分组、投影、匿名类型、投影与匿名类型
- 本章节笔记中,需要如下元数据
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo02
{
class Employee
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public int Salay { get; set; }
public override string ToString()
{
return $"Id = {Id}, Name = {Name}, Age = {Age}, Gender = {Gender}, Salary = {Salay}";
}
}
class Program
{
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salay = 5000 });
list.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salay = 3000 });
list.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salay = 9000 });
list.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salay = 2000 });
list.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salay = 1000 });
list.Add(new Employee { Id = 6, Name = "nancy", Age = 36, Gender = false, Salay = 8000 });
list.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salay = 8500 });
list.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salay = 8000 });
}
}
}
1. 聚合函数(视频Part2-19)
- Max()、Min()、Average()、Sum()、Count()
- LINQ中所有的扩展方法几乎都是针对IEnumerable接口的,而几乎所有能返回集合都返回IEnumerable,所以是可以把几乎所有方法链式调用的。
list.Where(e => e.Age > 30).Min(e => e.Salary); // 获取年龄大于30的,工资最大值。
list.Max(e => e.Name); // 获取字符串大小最大值。
double a = list.Where(e => e.Age >=30).Average(e => e.Salary); // 获取年龄大于等于30,平均工资返回给a。
2. 分组
- GroupBy方法参数是分组条件表达式、返回值为IGrouping<Tkey, TSource>类型的泛型IEnumerable。
- 也就是每一组以一个IGrouping对象的形式返回。
- IGrouping是一个继承自IEnumerable的接口,IGrouping中key属性表示这一组的分组数据的值。
- 可以用var简化编程。
// 关注返回值类型
static void Main(string[] args)
{
IEnumerable<IGrouping<int, Employee>> items = list.GroupBy(e => e.Age); // 按照Age分组,然后里面还要套一层可迭代对象。
foreach (IGrouping<int, Employee> it in items)
{
Console.WriteLine(it.Key); // Key表示分组的值。
foreach (Employee ii in it) // it此时也是一个可迭代对象
{
Console.WriteLine(ii);
}
}
Console.ReadLine();
}
控制台输出:
28
Id = 1, Name = jerry, Age = 28, Gender = True, Salary = 5000
33
Id = 2, Name = jim, Age = 33, Gender = True, Salary = 3000
Id = 8, Name = jack, Age = 33, Gender = True, Salary = 8000
35
Id = 3, Name = lily, Age = 35, Gender = False, Salary = 9000
Id = 7, Name = zack, Age = 35, Gender = True, Salary = 8500
16
Id = 4, Name = lucy, Age = 16, Gender = False, Salary = 2000
25
Id = 5, Name = kimi, Age = 25, Gender = True, Salary = 1000
36
Id = 6, Name = nancy, Age = 36, Gender = False, Salary = 8000
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 使用类型推断的写法
static void Main(string[] args)
{
var items = list.GroupBy(e => e.Age);
foreach (var it in items)
{
Console.WriteLine(it.Key);
foreach (var ii in it)
{
Console.WriteLine(ii);
}
}
Console.ReadLine();
}
控制台输出:
28
Id = 1, Name = jerry, Age = 28, Gender = True, Salary = 5000
33
Id = 2, Name = jim, Age = 33, Gender = True, Salary = 3000
Id = 8, Name = jack, Age = 33, Gender = True, Salary = 8000
35
Id = 3, Name = lily, Age = 35, Gender = False, Salary = 9000
Id = 7, Name = zack, Age = 35, Gender = True, Salary = 8500
16
Id = 4, Name = lucy, Age = 16, Gender = False, Salary = 2000
25
Id = 5, Name = kimi, Age = 25, Gender = True, Salary = 1000
36
Id = 6, Name = nancy, Age = 36, Gender = False, Salary = 8000
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 获取每组中最大工资
static void Main(string[] args)
{
var items = list.GroupBy(e => e.Age);
foreach (var it in items)
{
Console.WriteLine(it.Key);
Console.WriteLine("最大工资:" + it.Max(e => e.Salay));
foreach (var ii in it)
{
Console.WriteLine(ii);
}
}
Console.ReadLine();
}
控制台输出
28
最大工资:5000
Id = 1, Name = jerry, Age = 28, Gender = True, Salary = 5000
33
最大工资:8000
Id = 2, Name = jim, Age = 33, Gender = True, Salary = 3000
Id = 8, Name = jack, Age = 33, Gender = True, Salary = 8000
35
最大工资:9000
Id = 3, Name = lily, Age = 35, Gender = False, Salary = 9000
Id = 7, Name = zack, Age = 35, Gender = True, Salary = 8500
16
最大工资:2000
Id = 4, Name = lucy, Age = 16, Gender = False, Salary = 2000
25
最大工资:1000
Id = 5, Name = kimi, Age = 25, Gender = True, Salary = 1000
36
最大工资:8000
Id = 6, Name = nancy, Age = 36, Gender = False, Salary = 8000
说明:如果使用SQL语句,上述表达式写为:select age.max(salary) from t group by age;
3. 投影(视频Part2-10)
- select方法是用来进行投影操作的,把集合中的每一项转换为另外一种类型。
IEnumerable<int> ages = list.Select(e => e.Age) // 只取Age
IEnumerable<string> strings = list.Select(e => e.Name + "," + e.Age); // 获取姓名、年龄,并返回拼接后的字符串
IEnumerable<string> strings = list.Where(e => e.Age > 30).Select(e => e.Name + "," + e.Age); // 过滤后再获取姓名、年龄,并返回拼接后的字符串
IEnumerable<string> names = list.Select(e => e.Gender? "男": "女"); // 注意Gender是bool类型,而此时Select返回的是男、女,属于字符串类型。
var dogs = list.Select(p => new Dog{NickName = e.Name, Age = e.Age}); // 将当前集合中的数据转换为另一种类型。
说明:
1. C#中使用Select方法来进行投影操作;而在SQL语句中select操作,也可以理解为投影操作。
2. Select方法就是一条一条的按照()里面表达式的方式来进行转换。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 将Employee类型的集合投影成Person类型的集合中
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo02
{
class Employee
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public int Salay { get; set; }
public override string ToString()
{
return $"Id = {Id}, Name = {Name}, Age = {Age}, Gender = {Gender}, Salary = {Salay}";
}
}
class Person
{
public string PersonName { get; set; }
public int PersonAge { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salay = 5000 });
list.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salay = 3000 });
list.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salay = 9000 });
list.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salay = 2000 });
list.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salay = 1000 });
list.Add(new Employee { Id = 6, Name = "nancy", Age = 36, Gender = false, Salay = 8000 });
list.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salay = 8500 });
list.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salay = 8000 });
IEnumerable<Person> items = list.Select(e => new Person { PersonName = e.Name, PersonAge = e.Age });
foreach (Person it in items)
{
Console.WriteLine($"PersonName = {it.PersonName}, Age = {it.PersonAge}");
}
Console.ReadLine();
}
}
}
控制台输出:
PersonName = jerry, Age = 28
PersonName = jim, Age = 33
PersonName = lily, Age = 35
PersonName = lucy, Age = 16
PersonName = kimi, Age = 25
PersonName = nancy, Age = 36
PersonName = zack, Age = 35
PersonName = jack, Age = 33
4. 匿名类型
var p1 = new {Name = "Tom", Id = 1};
var P2 = new {Name, Id = 1, p1.Age};
说明:通过反编译查看匿名类型的原理
※※※var的高光时刻。※※※
4.1 什么是匿名类型
// @1 声明一个Person类型
class Person
{
public string PersonName { get; set; }
public int PersonAge { get; set; }
}
// @2 创建Person类型的对象
Person p1 = new Person { PersonName = "Qinway", PersonAge = 20};
说明:
1. 在@1处先声明一个Person类型,该类型定义了2个属性。
2. 在@2处,创建了一个Person类型变量p1,并给属性赋值。等号右边显式的指明了Person类型对象,等号左边显式的声明了Person类型变量。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// @3 直接不通过声明类型来创建变量
using System;
namespace Demo02
{
class Program
{
static void Main(string[] args)
{
var obj1 = new { Name = "Qin", Age = 30, BBB = 99}; // @3.1 声明一个var类型的变量obj1
Console.WriteLine($"Name = {obj1.Name}, Age = {obj1.Age}, BBB = {obj1.BBB}"); // @3.2 打印类型的属性。
Console.ReadLine();
}
}
}
控制台输出:
Name = Qin, Age = 30, BBB = 99
说明:
1. 没有声明类型
2. 在@3.1处,等号右边直接创建了对象;并且在@3.2处输出该对象的属性。
3. 在@3.1处,编译器编译的时候,创建了一个匿名的类型。这个匿名类型定义了三个属性,并且创建了这个匿名类型的对象,然后赋值给变量obj1。
4. 在@3.1处,等号左边必须写var,因为是匿名类型,这个类型没有名字,只能用var推断类型。
注意
1. 在@3.1处创建了对象,说明这个类型是存在的,它只是没有名字匿名类型,在编译器编译的时候会将这段代码转换成一个匿名类型。并不是说这个类型不确定,并不改变C#为强类型语言这个特性。
2. 这里变量不能声明为Object类型,如果用Object类型的变量,是不能通过obj1.Name、obj.Age来获取数据的。
4.2 查看反编译的结果
- 如下图,反编译后的匿名类型
- 如下图,反编译后的Main方法所在的类
4.3 匿名类型总结
var obj1 = new { Name = "Qin", Age = 30, BBB = 99};
说明:
1. 上述代码段在源代码中看不出来匿名类型的声明。
2. 通过反编译发现存在匿名类型<>f__AnonymousType0<<Name>j__Tpar, ....>>。
3. 匿名类型是在编译过程中完成的,即在编译时生成了匿名类型,然后给该类型的各个属性赋值。
4. 匿名类型最终编译完成之后,任然是一个确定的类型,只不过这个类型的名字是由编译器来定的。
5. 投影与匿名类型
var items = list.Select(e => new {e.Name, e.Age, XingBie = e.Gender? "男" : "女"});
var items = list.GroupBy(e => e.Gender).Select(g => new {Gender = g.Key, Count = g.Count(), AvgSalary = g.Average
(e => e.Salary), MinAge = g.Min(e => e.Age)});
5.1 将当前类型集合投影到匿名类型集合
// 将当前类型集合投影到匿名类型集合
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo02
{
class Employee
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public int Salay { get; set; }
public override string ToString()
{
return $"Id = {Id}, Name = {Name}, Age = {Age}, Gender = {Gender}, Salary = {Salay}";
}
}
class Program
{
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salay = 5000 });
list.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salay = 3000 });
list.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salay = 9000 });
list.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salay = 2000 });
list.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salay = 1000 });
list.Add(new Employee { Id = 6, Name = "nancy", Age = 36, Gender = false, Salay = 8000 });
list.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salay = 8500 });
list.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salay = 8000 });
var items1 = list.Select(e => new { XingMing = e.Name, NianLing = e.Age, XingBie = e.Gender ? "男" : "女" });
foreach (var it in items1)
{
Console.WriteLine(it.XingMing + it.NianLing + it.XingBie);
}
Console.ReadLine();
}
}
}
控制台输出:
jerry28男
jim33男
lily35女
lucy16女
kimi25男
nancy36女
zack35男
jack33男
5.2 投影与匿名类型综合运用
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo02
{
class Employee
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public int Salay { get; set; }
public override string ToString()
{
return $"Id = {Id}, Name = {Name}, Age = {Age}, Gender = {Gender}, Salary = {Salay}";
}
}
class Program
{
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salay = 5000 });
list.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salay = 3000 });
list.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salay = 9000 });
list.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salay = 2000 });
list.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salay = 1000 });
list.Add(new Employee { Id = 6, Name = "nancy", Age = 36, Gender = false, Salay = 8000 });
list.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salay = 8500 });
list.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salay = 8000 });
var items1 = list.GroupBy(e => e.Age).Select( // @1
g => new { NianLing = g.Key, MaxS = g.Max(e => e.Salay), MinS = g.Min(e => e.Salay), RenSu = g.Count()}); // @2
foreach (var it in items1)
{
Console.WriteLine(it.NianLing + "," + it.MaxS + "," + it.MinS + "," + it.RenSu);
}
Console.ReadLine();
}
}
}
说明:
1. 在@1处,先通过年龄分组。
2. 在@2处,将现有集合,先通过聚合函数获取值,然后映射到匿名类型集合。
Tips:老师提到本例能理解,那么LINQ也就没啥难的了。不知道是否是在宽慰我,我感觉这里并不难理解。
结尾
书籍:ASP.NET Core技术内幕与项目实战
视频:https://www.bilibili.com/video/BV1pK41137He
著:杨中科
ISBN:978-7-115-58657-5
版次:第1版
发行:人民邮电出版社
※敬请购买正版书籍,侵删请联系85863947@qq.com※
※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

浙公网安备 33010602011771号