C# 中 Lambda、Linq 与 Expression

1.匿名函数

匿名函数是 C# 中无需显式定义方法名的代码块,可直接赋值给委托,大幅减少冗余代码,是 Lambda 表达式的前身。

  • 定义匹配签名的委托类型
  • 使用 delegate 关键字直接声明匿名代码块
  • 将匿名函数赋值给委托变量并执行

匿名函数本质是编译器生成的无名方法,无需在类中单独定义具名方法,可直接捕获上下文变量,适合短生命周期的临时逻辑传递。

cs 复制代码
// 1. 定义委托签名
delegate int MathOperation(int a, int b);

// 2. 声明匿名函数并赋值给委托
MathOperation multiply = delegate (int x, int y)
{
    return x * y;
};

// 3. 执行匿名函数
int result = multiply(3, 4);
Console.WriteLine($"3*4的结果:{result}"); // 输出 12

// 结合集合筛选场景
int[] numbers = { 1, 2, 3, 4, 5, 6 };
var evenNums = Array.FindAll(numbers, delegate (int n)
{
    return n % 2 == 0;
});
// 输出 2 4 6
foreach (var num in evenNums) Console.WriteLine(num);

2. Lambda 表达式

Lambda 是匿名函数的极简语法形式,用 => 符号替代 delegate 关键字,语法更简洁,是 Linq 与 Expression 的核心语法载体。

  • 确认目标委托的参数数量与类型
  • 按简化规则编写 Lambda 表达式
  • 赋值给委托或直接作为方法参数传入

Lambda 可自动推断参数类型,单参数时可省略括号,单表达式时可省略大括号与 return,大幅压缩代码量,同时保留强类型检查特性。

cs 复制代码
// 完整写法 → 逐步简化
// 1. 完整Lambda写法
Func<int, int, int> add1 = (int a, int b) => { return a + b; };
// 2. 省略参数类型(编译器自动推断)
Func<int, int, int> add2 = (a, b) => { return a + b; };
// 3. 单表达式省略大括号与return
Func<int, int, int> add3 = (a, b) => a + b;

// 单参数场景进一步简化
Action<string> printMsg = msg => Console.WriteLine($"消息:{msg}");
printMsg("Hello Lambda");

// 结合集合筛选
List<string> names = new List<string> { "Alice", "Bob", "Anna", "David" };
var aNames = names.Where(name => name.StartsWith("A"));
// 输出 Alice Anna
foreach (var name in aNames) Console.WriteLine(name);

3. Linq 表达式

Linq 是 .NET 内置的语言集成查询能力,提供声明式语法统一操作各类数据源,彻底解决不同数据场景下的语法割裂问题。

  • 引入 System.Linq 命名空间
  • 选择查询表达式语法或方法语法编写查询逻辑
  • 执行查询获取结果

Linq 提供两种等价语法:类似 SQL 的查询表达式语法可读性更强,基于 Lambda 的方法语法更灵活,二者编译后生成的 IL 几乎完全一致。

cs 复制代码
// 准备测试数据
int[] scores = { 97, 92, 81, 60, 75 };

// 写法1:查询表达式语法
var highScoresQuery = from score in scores
                      where score > 80
                      orderby score descending
                      select score;

// 写法2:方法语法(等价效果)
var highScoresMethod = scores
    .Where(score => score > 80)
    .OrderByDescending(score => score);

// 执行查询输出结果
// 输出 97 92
foreach (var s in highScoresQuery) Console.WriteLine(s);

4. 扩展方法

扩展方法可在不修改原有类源码的前提下,为已有类型添加新方法,是 Linq 链式调用能力的底层支撑机制。

  • 定义静态非嵌套的扩展类
  • 编写静态方法,第一个参数用 this 关键字标记要扩展的类型
  • 引入扩展类的命名空间,即可像实例方法一样调用

扩展方法本质是静态方法的语法糖,编译器会将实例调用形式转换为静态方法调用,不会修改原类型的源码与结构。

cs 复制代码
// 定义扩展类(必须是静态类)
namespace CustomExtensions
{
    public static class StringExtension
    {
        // 为string类型扩展统计单词数量的方法
        public static int WordCount(this string str)
        {
            return str.Split(new char[] { ' ', '.', '?' }, 
                StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }
}

// 调用端代码
using CustomExtensions;
string testStr = "The quick brown fox jumped over the lazy dog.";
int count = testStr.WordCount();
Console.WriteLine($"单词总数:{count}"); // 输出 9

5. Linq to Object

Linq to Object 是直接对内存集合(数组、List、Dictionary 等)执行 Linq 查询的模式,无需额外适配器,是日常开发最常用的 Linq 场景。

  • 引入 System.Linq 命名空间
  • 针对内存集合编写链式查询逻辑
  • 调用 ToList() / ToArray() 等方法立即执行查询

Linq to Object 基于 IEnumerable<T> 接口实现,具备延迟执行特性:查询逻辑在遍历结果时才真正执行,可通过立即执行方法避免意外重复计算。

cs 复制代码
// 定义测试实体
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 准备测试集合
List<Person> people = new List<Person>
{
    new Person { Name = "Alice", Age = 25 },
    new Person { Name = "Bob", Age = 30 },
    new Person { Name = "Charlie", Age = 35 },
    new Person { Name = "Anna", Age = 22 }
};

// 链式查询:筛选年龄>25 → 按年龄排序 → 提取姓名
var result = people
    .Where(p => p.Age > 25)
    .OrderBy(p => p.Age)
    .Select(p => p.Name)
    .ToList();
// 输出 Bob Charlie
foreach (var name in result) Console.WriteLine(name);

6. Expression表达式 动态拼接表达式目录树

Expression 表达式目录树是可遍历、可修改的代码抽象语法树,支持在运行时动态构建查询逻辑,是动态查询场景的核心方案。

  • 引入 System.Linq.Expressions 命名空间
  • 用 ParameterExpression 定义查询参数节点
  • 逐层拼接常量、属性访问、二元运算等子节点
  • 用 Expression.Lambda 封装为完整 Lambda 表达式树

表达式树不是可直接执行的代码,而是描述代码结构的对象树,每个节点都是强类型的 Expression 子类,可手动组合出任意复杂的查询逻辑。

cs 复制代码
using System.Linq.Expressions;

// 目标:动态构建 p => p.Age >= 30 表达式
// 1. 定义参数节点:Person类型的参数p
ParameterExpression param = Expression.Parameter(typeof(Person), "p");

// 2. 拼接子节点:访问p.Age属性
MemberExpression ageProp = Expression.Property(param, "Age");

// 3. 拼接常量节点:30
ConstantExpression ageConst = Expression.Constant(30);

// 4. 拼接二元运算节点:p.Age >= 30
BinaryExpression greaterThan = Expression.GreaterThanOrEqual(ageProp, ageConst);

// 5. 封装为完整Lambda表达式树
Expression<Func<Person, bool>> lambdaExpr = 
    Expression.Lambda<Func<Person, bool>>(greaterThan, param);

// 编译为委托后执行
var filter = lambdaExpr.Compile();
var filteredPeople = people.Where(filter).ToList();
// 输出 Bob Charlie
foreach (var p in filteredPeople) Console.WriteLine(p.Name);

7. Expression解析表达式

表达式树的节点完全可遍历,可通过递归访问所有节点,实现表达式的读取、修改、转换等高级操作,是规则引擎、ORM 翻译的核心能力。

  • 继承 ExpressionVisitor 自定义访问器
  • 重写对应节点类型的访问方法
  • 递归遍历整棵表达式树,完成解析或修改

.NET 内置的 ExpressionVisitor 提供了所有节点类型的默认访问实现,重写对应方法即可拦截节点访问过程,实现自定义解析逻辑。

cs 复制代码
// 自定义访问器:解析表达式树并打印节点结构
public class ExpressionPrinter : ExpressionVisitor
{
    protected override Expression VisitBinary(BinaryExpression node)
    {
        Console.WriteLine($"二元运算:{node.NodeType}");
        return base.VisitBinary(node);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        Console.WriteLine($"访问成员:{node.Member.Name}");
        return base.VisitMember(node);
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        Console.WriteLine($"常量值:{node.Value}");
        return base.VisitConstant(node);
    }
}

// 使用解析器遍历之前构建的表达式树
var printer = new ExpressionPrinter();
printer.Visit(lambdaExpr);
// 输出:
// 访问成员:Age
// 常量值:30
// 二元运算:GreaterThanOrEqual

8. Expression扩展linq to everything

基于表达式树的动态拼接能力,可封装通用查询扩展方法,实现任意字段、任意条件的动态查询,适配 EF Core、内存集合等各类数据源,真正做到 Linq to Everything。

  • 定义通用查询条件实体,封装字段名、操作符、值
  • 编写条件解析方法,将条件转换为表达式树节点
  • 用 Expression.AndAlso / OrElse 组合多个条件
  • 封装为 IQueryable 的扩展方法,直接链式调用

基于表达式树的动态查询不会生成硬编码逻辑,可适配任意实体类型,同时保留强类型检查,避免 SQL 注入风险,大幅减少重复查询代码。

cs 复制代码
// 定义通用查询条件类
public class QueryCondition
{
    public string FieldName { get; set; }
    public object Value { get; set; }
    public string Operator { get; set; } // Equal/GreaterThan/LessThan
}

// 定义IQueryable的动态查询扩展方法
public static class LinqExtensions
{
    public static IQueryable<T> DynamicWhere<T>(
        this IQueryable<T> source, 
        List<QueryCondition> conditions)
    {
        if (conditions == null || conditions.Count == 0) return source;
        
        ParameterExpression param = Expression.Parameter(typeof(T), "x");
        Expression body = Expression.Constant(true);

        foreach (var cond in conditions)
        {
            MemberExpression prop = Expression.Property(param, cond.FieldName);
            ConstantExpression val = Expression.Constant(cond.Value);
            BinaryExpression conditionExpr = cond.Operator switch
            {
                "Equal" => Expression.Equal(prop, val),
                "GreaterThan" => Expression.GreaterThan(prop, val),
                "LessThan" => Expression.LessThan(prop, val),
                _ => throw new NotSupportedException("不支持的操作符")
            };
            body = Expression.AndAlso(body, conditionExpr);
        }

        var lambda = Expression.Lambda<Func<T, bool>>(body, param);
        return source.Where(lambda);
    }
}

// 调用示例:动态组合多个条件
var query = people.AsQueryable()
    .DynamicWhere(new List<QueryCondition>
    {
        new QueryCondition{ FieldName = "Age", Value = 20, Operator = "GreaterThan" },
        new QueryCondition{ FieldName = "Name", Value = "Alice", Operator = "Equal" }
    })
    .ToList();
// 输出 Alice
foreach (var p in query) Console.WriteLine(p.Name);
相关推荐
zhanghaha13141 小时前
C# 系列教程_7 判断语句(分支)保姆级精讲
开发语言·c#
zhanghaha13142 小时前
C# 系列教程_8 循环语句 保姆级精讲(面向新手)
开发语言·c#
rockey62711 小时前
AScript之LINQ扩展
c#·.net·script·动态脚本
1-1=017 小时前
C# 委托与事件
开发语言·c#
1-1=01 天前
C# 特性(Attribute)
c#·.net·.netcore
ly76891 天前
幂等消费者与重试副作用:Kafka 端点接入时的去重边界
分布式·kafka·linq·apache camel·幂等消费者·重试副作用·去重边界
淡海水2 天前
10-01-互操作-C#与C++边界的内存管理
unity·c#·游戏引擎·.netcore·gc
WarPigs2 天前
C#把byte[]以十六进制打印,带分割符
c#
暴走约伯2 天前
【VFX工具链源码拆解 04】拯救丢失的绑定:Timeline 一键克隆与层级相对路径重映射算法
unity·c#·游戏引擎·硬件工程