学习前置(大概了解就可以):
匿名方法 是 C# 中一种没有名字的方法 ,它允许你直接内联定义代码块 ,无需单独声明方法名,主要用于简化委托(Delegate)的使用,是 Lambda 表达式的前身。
一、核心概念
- 依赖委托:匿名方法必须绑定到委托类型使用,不能单独存在;
- 无方法名:只有方法体,没有方法名称;
- 简化代码:无需单独定义独立方法,适合一次性、简短的逻辑。
格式:
cs
// 委托定义(匿名方法必须依附委托)
委托类型 委托变量 = delegate(参数列表)
{
// 方法体逻辑
};
例子
cs
// Action:无参数无返回值
Action print = delegate() { Console.WriteLine("内置委托+匿名方法"); };
// Func:带参数有返回值
Func<int, int, int> multiply = delegate(int a, int b) { return a * b; };
Lambda学习内容:
Lambda 表达式是简化版的匿名方法 ,用来快速定义可传递的代码块(委托 / 表达式树),是 C# 中最常用的语法之一,广泛用于 LINQ 查询、事件、委托、异步编程等场景。
一、Lambda核心概念
- 本质:没有方法名的微型方法
- 符号 :
=>读作 goes to / 变成 - 作用:代替冗长的委托 / 匿名方法写法,让代码更简洁
- 依赖 :必须配合委托 (如
Action、Func)或 LINQ 使用
二、Lambda基本格式
cs
(参数,参数) =>
{
要做的事;
}
可以写在一行
(参数,参数,参数) => { 要做的事; }
参数多少主要看委托,而且系统可以从委托里推导出参数的类型所以参数类型也可以不写
比如(int x,int b)=>{};可以写成(a,b)=>{}
可以简化很多东西
Func<int, int> f = new Func<int, int>((int x) => { return x + 1; });
第 1 步:去掉 new Func<int, int> 构造
委托可以直接赋值匿名方法 / Lambda,不需要手动 new
Func<int, int> f = (int x) => { return x + 1; };
第 2 步:去掉参数类型 int
编译器能从委托 Func<int, int> 推导出 x 是 int
Func<int, int> f = (x) => { return x + 1; };
第 3 步:单个参数去掉括号
只有一个参数时,(x) 可以写成 x
Func<int, int> f = x => { return x + 1; };
最后一步:单行表达式去掉大括号和 return
Lambda 右边只有一行返回语句时:去掉 { },去掉 return,去掉末尾分号
Func<int, int> f = x => x + 1;
可以看到省略之后的x=>x+1没有;
因为省略之前的lambda是作为一个完整的方法体;
省略之后的lambda表达式只是作为一个参数;new Func<int, int>( 这里面是参数 );
就像正常方法里正常语句里的参数一样
void Add(int x)
{
}
调用方法 Add(1+5);这里的1+5就是表达式不用加;变成Add(1+5;);
2.1. 无参数 Lambda
cs
// 完整写法
Action test = () => { Console.WriteLine("Hello Lambda"); };
// 只有一行代码时,大括号可以省略
Action test2 = () => Console.WriteLine("Hello Lambda");
//自定义委托
public delegate void Test();
Test tt = new Test(() => { Console.WriteLine("Hello Lambda"); });
tt();
2.2. 带参数 和返回值Lambda
cs
// 单行表达式:自动返回结果,不用写 return
Func<int, string> intToString = num => num.ToString();
//多参数保留()
Func<int, int,string> intToString = (num ,num1)=> num.ToString();
// 多行代码:必须写 return + 大括号
Func<int, int> calc = n =>
{
n += 10;
return n * 2;
};
public delegate int de(int xxx, int x);
de d = new de((int a, int b) =>
{
Console.WriteLine(a + b);
return a + b;
});
2.3带参数无返回值
cs
Action<string, int> aa = new Action<string, int>(( x, b) => {
Console.WriteLine(x+b); });
aa("sss", 5);
一个比较复杂的例子用到了泛型委托;
cs
static async Task Main(string[] args)
{
Add((int a,int b) => {
Console.WriteLine(a*b);
return a * b; },7,8);
}
static void Add<T>(Func<T,T,T> func,T A,T B )
{
func(A, B);
}
三、Linq
3.1 linq是什么
LINQ(Language Integrated Query,语言集成查询) 是 C# 中最核心、最实用 的功能之一,它让你可以用类似 SQL 的语法直接查询、过滤、排序、聚合各种数据(数组、集合、数据库、XML 等),无需编写复杂的循环逻辑。
3.2 linq里的lambda为什么不用;
有一个疑问就是同样是lumbda表达式为什么在委托里加;但是linq查询里不用;
cs
// 多行代码的时候可以看到lambda表达式的语句里要加;
Func<int, int> calc = n =>
{
n += 10;
return n * 2;
};
// 这里去掉()之后已经初见端倪了这里的;也不是加个lambda的是加给整个语句的
Func<int, string> intToString = num => num.ToString();
linq的时候lambda表达式作为参数没有;
var query2 = students.Where(s => s.Age == 20);
因为linq里的lambda表达式只是作为一个参数;就像正常方法里正常语句里的参数一样
cs
void Add(int x)
{
}
调用方法 Add(1+5);这里的1+5就是表达式不用加;变成Add(1+5;);
3.3常用方法
首先创建一个list
cs
// 学生实体类
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string ClassName { get; set; }
public int Score { get; set; }
}
// 测试数据
List<Student> students = new List<Student>
{
new Student{Id=1, Name="张三", Age=18, ClassName="一班", Score=90},
new Student{Id=2, Name="李四", Age=19, ClassName="一班", Score=85},
new Student{Id=3, Name="王五", Age=18, ClassName="二班", Score=95},
new Student{Id=4, Name="赵六", Age=20, ClassName="二班", Score=78},
new Student{Id=5, Name="钱七", Age=19, ClassName="三班", Score=88}
};
3.3. 1. 过滤,筛选:Where(最常用)
注意这里查出来之后x并不是list或者Stundent类;而是IEnumerable<Student>这只是一个「待执行的查询规则」 ,不是真正装满数据的集合。只是记录了你要筛选 "年龄 = 18" 的学生这个条件。
因为linq是可以拼接的可以在这个语句后面加students.Where(...).OrderBy(...).Select(...),每一步都不执行,最后一次性执行,性能更高。
cs
//查询Age大于等于19岁的
var x= stu.Where(s => s.Age >= 19);
// 多条件:一班 且 分数>85
var list2 = students.Where(s => s.ClassName == "一班" && s.Score > 85);
如果这时候要满足结果的list加个tolist方法
// 这才是真正的 List<Student>
List<Student> x= stu.Where(s => s.Age >= 19).ToList();
3.3.2 select 提取
select也是筛选和where的区别:
1.select是提取对象的具体一个东西,where是一堆对象里筛选合格的对象
2.想 删数据、留一部分 → 用 Where,想 改数据、转格式、取字段 → 用 Select
cs
//获取所有学生的 姓名列表(只要名字,不要其他信息)
var x = stu.Select(s => s.Name).ToList();
select还可以新建对象,格式是.select(顺便的参数名字=> new{ 名字1=顺便的参数名字.属性,名字1=顺便的参数名字.属性 });
✅ 一个临时的、自动生成的类的对象
✅ 没有名字的类(所以叫 "匿名")
✅ 编译器自动帮你创建,你不用写 class 定义
✅ 只读对象(创建后不能改里面的值)
//找出 二班 的学生,并且只返回姓名和分数
var x = stu.Where(s=>s.ClassName=="二班").Select(s => new { mz=s.Name, fs=s.Score }).ToList();
x得到的就是一个类的list里面的类只有两个属性mz和fs
可以写成下面这样比较好理解new的格式,因为是系统生成没面子的类所以直接是new {}中间没有名字,
还有就是为什么是,"mz=s.Name,"还是因为是参数,看下面完整的例子比较好理解
var x = stu.Where(s=>s.ClassName=="二班").Select(s => new Student{ Name=s.Name, Score=s.Score }).ToList();
//返回所有学生的 姓名 +成绩等级等级:≥90 = 优秀,否则 = 普通
var x = stu.Select(s => new { mz= s.Name,dj=s.Score>=90?"优秀":"普通"}).ToList();
3.3.3排序OrderBy
OrderBy → 从小到大, OrderByDescending → 从大到小;
cs
// 单字段排序
var sorted = products.OrderBy(p => p.Price);
var sortedDesc = products.OrderByDescending(p => p.Price);
// 多字段排序
var multiSorted = products
.OrderBy(p => p.Category) // 先按分类升序
.ThenByDescending(p => p.Price); // 再按价格降序
// 反转顺序
var reversed = products.Reverse();
3.3.4GroupBy = 分组
作用:把数据按照某个特征(比如班级、性别、年龄)分成一堆一堆的
最常用场景:按班级统计人数、算平均分、求最高分
cs
List<Student> stu = new List<Student>
{
new Student{Id=1, Name="张三", Age=18, ClassName="一班", Score=90},
new Student{Id=2, Name="李四", Age=19, ClassName="一班", Score=85},
new Student{Id=3, Name="王五", Age=18, ClassName="二班", Score=95},
new Student{Id=4, Name="赵六", Age=20, ClassName="二班", Score=78},
new Student{Id=5, Name="钱七", Age=19, ClassName="三班", Score=88}
};
//按【班级】分组
var group = stu.GroupBy(s => s.ClassName).ToList();
// group 的类型是:IEnumerable<IGrouping<string, Product>>;
不是 List<T>,也不是 Dictionary<TKey, List<T>> ;可以理解为有
两个东西一个是key这个属性还有一个就是符合这个key的数据集合;比如
第一个key是一班,集合里就是张三和李四这两个对象
可以靠 var i = group [0].Key;获取第一个标签 ,
var i = group [0].Tolist();把符合第一个标签的集合变成list
// 万能模板
var result = source
.GroupBy(x => x.KeyProperty) // 分组
.Select(g => new // 投影
{
Key = g.Key,
Count = g.Count(),
Sum = g.Sum(x => x.ValueProperty),
Items = g.ToList()//这就是把符合第一个标签的集合变成list
});


3.3.5常见数学计算
- Count() → 统计数量
- Sum() → 求和
- Average() → 平均
- Max() / Min() → 最大 / 最小
都可以加入判断比如下面的求平均年龄可以加一个只对年龄大于19的求
cs
var x = stu.Count();//list的数量
var x = stu.Select(s=>s.Age).Sum(); // 总年龄
var x = stu.Select(s=>s.Age).Average(), // 平年龄
var x = stu.Select(s=>s.Age).Average(a=>a>19);
var x = stu.Select(s=>s.Age).Max() // 最高分
3.3.6 Any / All ------ 判断是否存在 / 全部满足
专门用来做条件判断 ,返回的只有:true(是) 或 false(否)
Any = 至少有一个满足 → true
只要有一个符合条件,就返回 true
All = 全部都满足 → true
所有的都必须符合,才返回 true
cs
// 判断 一班 里 有没有人 分数 ≥ 90
var x = stu.Where(s => s.ClassName == "一班").Any(y => y.Score >= 90);
3.3.7 合并Concat和Union
两个都是合并
- Concat :直接拼接,保留重复
- Union :合并 + 自动去重
cs
// 合并 list1 + list2
var 全部学生 = list1.Concat(list2).ToList();
linq综合例子
cs
//查询 分数大于 85 分 并且 不是二班 的学生。结果按照 年龄降序 排列(大的在前)
。最后只返回:姓名、班级、分数。
var x = stu.Where(s => s.Score > 85 && s.ClassName != "二班")
.OrderByDescending(ss => ss.Age).Select(sss => new { xm = sss.Name,
bj = sss.ClassName, fs = sss.Score }).ToList();
//按 班级 分组只查询 该班级总人数 >= 2 人 的班级返回:班级名、人数、最高分、
最低分
var x = stu.GroupBy(x => x.ClassName).Where(ss => ss.Count() >= 2)
.Select(sss => new { bj = sss.Key, rs = sss.Count(),
zg = sss.Max(s => s.Score), zd = sss.Min(s => s.Score) }).ToList();
学习时间:
26.0402