C# 学习笔记(12)—— Lambda 表达式

2023-10-20 18:51:01 浏览数 (2)

Lambda 表达式是 C# 3.0 中最重要的特性之一,另外一个同样重要的特性是 Linq

Lambda 表达式简介

Lambda 表达式可以理解为一个匿名方法,它可以包含表达式和语句,并且勇于创建委托或转换表达式树。在使用 Lambda 表达式时,都会使用 => 运算符(读作“goes to”),该运算符的左边是匿名方法的输入参数,右边则是表达式或语句块

Lambda 表达式的演变过程

大家都可以认为匿名方法是 Lambda 表达式的前世,下面代码掩饰了 Lambda 表达式从匿名方法一路演变而来的过程

代码语言:javascript复制
class Program
{
  static void Main(string[] args)
  {
    // C# 1.0 中创建委托实例的代码
    Func<string, int> delegateTest1 = new Func<string, int>(CallbackMethod);
    
    // C# 2.0 中用匿名方法来创建委托实例,此时不需要额外定义回调方法了
    Func<string, int> delegateTest2 = delegate(string text)
    {
      return text.length;
    };
    
    // C# 3.0 中使用 Lambda 表达式来创建委托实例
    Func<string, int> delegateTest3 = (string text) => text.length;
    
    // 此时也可以省略圆括号
    Func<string, int> delegateTest = text => text.length;
  }
}

虽然有了匿名方法,但是开发人员对此并不买账,很多人觉得用起来很麻烦,所以 C# 3.0 中又提出了 Lambda 表达式,替代匿名方法。尽管 Lambda 表达式完全颠覆了之前的编码风格,但是由于够简洁,一旦习惯上,就会喜欢上它

Lambda 表达式的使用

在实际开发过程中,委托的用途莫过于订阅事件了。为加深大家对 Lambda 表达式的理解,这里选择演示用 Lambda 表达式去订阅事件

首先给出的是 C# 3.0 之前的订阅代码,形成对比。具体的演示代码如下:

代码语言:javascript复制
using System;
Using System.Windows.Forms;

class Program
{
  static void Main(string[] args)
  {
    Button button1 = new Button();
    button1.Text = "点击我";
    
    // C# 2.0 中使用匿名方法来订阅事件
    button1.Click  = delegate(object sender, EventArgs e)
    {
      ReportEvent("Click事件", sender, e);
    };
    button1.KeyPress  = delegate(object sender, keyPressEventArgs e)
    {
      ReportEvent("KeyPress事件", sender, e);
    };
    
    // C# 3.0 前,初始化对象时会使用以下代码
    Form form = new Form();
    form.Name = "test";
    form.AutoSize = true;
    form.Controls.Add(button1);
    Application.Run(form);
  }
  
  private static void ReportEvent(string title, object sender, EventArgs e)
  {
    Console.WriteLine("发生的事件为:{0}", title);
  }
}

我们再来用 C# 3.0 的特性实现同样的效果:

代码语言:javascript复制
using System;
Using System.Windows.Forms;

class Program
{
  static void Main(string[] args)
  {
    Button button1 = new Button();
    button1.Text = "点击我";
    
    // C# 3.0 中使用 Lambda 表达式方式来订阅事件
    button1.Click  = (object sender, EventArgs e) => ReportEvent("Click事件", sender, e);
    button1.KeyPress  = (object sender, keyPressEventArgs e) => ReportEvent("KeyPress事件", sender, e);
    
    // C# 3.0 中使用对象初始化器
    Form form = new Form() { Name = "test", AutoSize = true, Controls = { button1 } };
    
    Application.Run(form);
  }
  
  private static void ReportEvent(string title, object sender, EventArgs e)
  {
    Console.WriteLine("发生的事件为:{0}", title);
  }
}

从以上代码可以看出,使用 C# 3.0 的对象初始化器和 Lambda 表达式,代码确实简洁了不少

表达式也有树结构——表达式树

Lambda 表达式除了可以用来创建委托,还可以转换为表达式树。表达式树是用来表达 Lambda 表达式逻辑的一种数据结构,它将代码表示成一个对象树,而非可执行的代码。你可以将表达式树理解为一种 数据结构,即类似数据结构的栈和队列,只不过表达式树泳鱼表示 Lambda 表达式的逻辑罢了

那么为什么要提出表达式目录树呢?主要是为了后面的 Linq to SQL作铺垫

动态地构造一个表达式树

代码语言:javascript复制
using System;
using System.Linq.Expressions;

namespace Demo
{

    class Program
    {
        static void Main(string[] args)
        {
            // 表达式参数
            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // 表达式主体部分
            BinaryExpression binary = Expression.Add(a, b);

            // 构建表达式树
            Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(binary, a, b);

            // 分析树结构,获取表达式树的主体部分
            BinaryExpression body = (BinaryExpression)expression.Body;

            // 左节点
            ParameterExpression left = (ParameterExpression)body.Left;

            // 右节点
            ParameterExpression right = (ParameterExpression)body.Right;
        }
    }
}

上面代码演示了通过一个表达式动态地构造表达式树对象,然后输出表达式树的结构、主体和左右节点的过程

通过 Lambda 表达式来构造表达式树

前面代码演示了动态地构造表达式树的方法,除此之外,你还可以直接使用 Lambda 表达式来构造表达式树,具体构造过程如下:

代码语言:javascript复制
using System;
using System.Linq.Expressions;

namespace Demo
{

    class Program
    {
        static void Main(string[] args)
        {
            // 构建表达式树
            Expression<Func<int, int, int>> expression = (a, b) => a   b;

            // 分析树结构,获取表达式树的主体部分
            BinaryExpression body = (BinaryExpression)expression.Body;

            // 左节点
            ParameterExpression left = (ParameterExpression)body.Left;

            // 右节点
            ParameterExpression right = (ParameterExpression)body.Right;
        }
    }
}

从以上代码可以看出,通过 Lambda 表达式来构造表达式树的过程非常简单,只需要把 Lambda 表达式树赋给一个表达式树变量即可

如何把表达式树转换成可执行代码

看完前面的代码,你肯定问:“表达式树是一种树形数据结构,但最终还是需要得到代码的执行结果,有没有一种发过誓把表达式树转换成可执行代码,然后输出执行成果呢?”

代码语言:javascript复制
using System;
using System.Linq.Expressions;

namespace Demo
{

    class Program
    {
        static void Main(string[] args)
        {
            // 构建表达式树
            Expression<Func<int, int, int>> expression = (a, b) => a   b;

            Func<int, int, int> delInstance = expression.Compile();
              
              int result = delInstance(2, 3);
          
              Console.WriteLine(result); // 5
        }
    }
}

以上代码通过Expression类的Compile方法将表达式树编译成委托实例,然后通过委托调用的方式得到了两个数的和

归纳总结

Lambda 表达式是 C# 3.0 最重要的特性之一,我们应该掌握它,以更好地学习和使用 Linq

0 人点赞