lambda和委托那点事

Lambda 

简介

      Lambda 表达式是一种可用于创建委托表达式目录树类型的一种匿名函数(匿名方法+Lambda)。通过使用 lambda 表达式,可以写入可作为参数传递或作为函数

调用值返回的本地函数。Lambda 表达式对于编写 LINQ 查询表达式特别有用,使用可以减少代码量(eg:分组、字典转化中),提高可读性(eg:用在递进式Linq中)。

      Lambda 表达式语法:(左侧指定输入参数,注:括号在只有一个参数下才是可选的)=> 右侧输入表达式或语句块。

应用

      定义一个动物实体类AnimalModel,预存储一些动物数据。

 /// <summary>
    /// 动物实体
    /// </summary>
    public class AnimalModel
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { set; get; }

        /// <summary>
        /// 重量
        /// </summary>
        public decimal Weight { set; get; }

        /// <summary>
        /// 是否是鸟类
        /// </summary>
        public bool isBird { set; get; }

        /// <summary>
        /// 寿命
        /// </summary>
        public decimal Lifetime { set; get; }
    }
View Code
 1 List<AnimalModel> animalInfo = new List<AnimalModel>();
 2 
 3         public Form1()
 4         {
 5             InitializeComponent();
 6             Lambda la = new Lambda();
 7             animalInfo = la.LoadAllAnimal();
 8             string allAnimals = string.Empty;
 9             foreach (var item in animalInfo)
10             {
11                 allAnimals += string.Format("名称:{0},  重量:{1},  是否是鸟类:{2},  寿命:{3}\r\n",
12                                    item.Name,
13                                    item.Weight,
14                                   (item.isBird ? "" : ""),
15                                    item.Lifetime);
16             }
17             txt_reveal.Text = allAnimals;
18         }
View Code

显示结果如图:

linq中应用

       Lambda可以取代匿名方法,除了一种情况:匿名方法提供了Lambda表达式中所没有的功能。匿名方法可用来忽略参数列表,可转换为具有各种签名的委托。

但在Linq中的应用中,Lambda相对于匿名委托有着绝对的优势。至于Lambda的性能问题,我个人觉得影响不大,只要代码中少出现类似“重复计算”或“.Count()>0判空

这种代码,性能影响是可接受的。

      用匿名委托和Lambda表达式分别实现一个筛选逻辑。可以看到Lambda明显比匿名方式更加简洁,更加人性化。在一个条件查询中都会看出差距,如果是涉及到

较复杂的筛选逻辑,Lambda的简洁和可读性优势更明显。

/// <summary>
        /// Lambda
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_lambda_Click(object sender, EventArgs e)
        {
            string animalName = txt_animal.Text;

            var seaAnimal = animalInfo.Where(p => p.Name.Contains(animalName)).ToList();

            string allAnimals = string.Empty;
            foreach (var item in seaAnimal)
            {
                allAnimals += string.Format("名称:{0},  重量:{1},  是否是鸟类:{2},  寿命:{3}\r\n",
                                  item.Name,
                                  item.Weight,
                                 (item.isBird ? "" : ""),
                                  item.Lifetime);
            }

            txt_reveal.Text += "Lambda查询结果:\r\n" + allAnimals;
        }
View Code
      /// <summary>
        /// 匿名
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_anonymity_Click(object sender, EventArgs e)
        {

            #region 匿名筛选动物

            string animalName = txt_animal.Text;

            //分配内存块+执行
            List<AnimalModel> seaAnimal = animalInfo.Where(
                     delegate(AnimalModel p)
                     {
                         return p.Name.Contains(animalName);
                     })
                     .ToList();

            //只有在调用匿名方法委托实例的时候才会执行匿名方法内部的操作
            //单纯的创建委托实例并不会立即执行匿名方法代码块。
            string allAnimals = string.Empty;
            foreach (var item in seaAnimal)
            {
                allAnimals += string.Format("名称:{0},  重量:{1},  是否是鸟类:{2},  寿命:{3}\r\n",
                                  item.Name,
                                  item.Weight,
                                  (item.isBird ? "" : ""),
                                  item.Lifetime);
            }
            txt_reveal.Text += "匿名查询结果:\r\n" + allAnimals;

            #endregion

            //需要分组 且 转换成 Dictionary的情况,用匿名就复杂多了
            var animalDic = animalInfo.GroupBy(delegate (AnimalModel p) { return p.Lifetime; })
                .ToDictionary(
                delegate (IGrouping<decimal, AnimalModel> g) { return g.Key; },
                delegate (IGrouping<decimal, AnimalModel> g)
                {
                    return g.OrderBy(delegate (AnimalModel s) { return s; }).ToList();
                });
           
        }
View Code

表达式树中应用

     表达式树也称表达式目录树,将代码以一种抽象的方式表示成一个对象树,树中每个节点本身都是一个表达式。表达式树不是可执行代码,它是一种数据结构

由4部分组成:Body 主体部分,Parameters 参数部分,NodeType 节点类型,Lambda表达式类型,是Linq(lambda+表达式树+扩展方法)的核心。

      语法:Expression<Func<type,returnType>> = (param) => lamdaexpresion;

      表达式树用途:

          1、用在动态编码中,效率比反射要高许多。但是Compile调用过程涉及动态代码生成,最好只调用一次,或者直接使用静态编码

          2、在LINQ to SQL中使用,我们需要将LINQ to SQL查询表达式(返回IQueryable类型)转换成表达式树。之所以需要转换是因为LINQ to SQL查询表达式

不是在C#代码中执行的,LINQ to SQL查询表达式被转换成SQL,通过网络发送,最后在数据库服务器上执行。

       现在来创建一个Lambda一个计算(w+y)*(x+y)的表达式树。

 //创建一个表达式树中的参数,作为一个节点,这里是最下层的节点
            ParameterExpression w = Expression.Parameter(typeof(int), "w");  
            ParameterExpression x = Expression.Parameter(typeof(int), "x");

            //这里w+y,生成表达式树中的一个节点,比上面节点高一级
            BinaryExpression wx = Expression.Add(w, x);

            //创建一个表达式树中的参数,作为一个节点,这里是最下层的节点
            ParameterExpression y = Expression.Parameter(typeof(int), "y");
            ParameterExpression z = Expression.Parameter(typeof(int), "z");

            //这里y+z,生成表达式树中的一个节点,比上面节点高一级
            BinaryExpression yz = Expression.Add(y, z);

            //运算两个中级节点,产生终结点
            BinaryExpression result1 = Expression.Multiply(wx, yz);

            Expression<Func<int, int, int, int, int>> lambda = Expression.Lambda<Func<int, int, int, int, int>>(result1, w, x, y, z);

            //将表达式树描述的lambda表达式,编译为可执行代码,并生成该lambda表达式的委托;
            Func<int, int, int, int, int> fa = lambda.Compile(); 

            txt_reveal.Text += fa(1, 1, 1, 1) + "\r\n"; //输出结果
View Code

编译器处理方式

       Lambda的定义就是:既可以用于创建委托,又可以用于创建表达式树的匿名函数。但是编译器对Lambda的处理逻辑又是怎么的?是生成可执行委托实例,还是

生成表达式树。取决于Lambda赋予的类型。当Lambda赋予委托变量时,编译器自动生成可执行委托实例;当Lambda赋予Expression变量时,编译器自动生成表达

式树。

委托

简介

      委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。是一种安全地封装方法的类型,它与 C 和C++ 中的函数指针类似。

与 C 中的函数指针不同,委托是面向对象的、类型安全的和保险的体现在对于函数指针所引用的函数指令块的类型检测,比如返回值,参数类型,参数个数)。

委托的类型由委托的名称定义。

      委托一般都是滞后调用,编写委托实现的人一般不知道具体何时该方法被调用。.net的委托是类且具有内建(内存机制)支持,可进行异步和广播式的调用。委托跟事件可自然的契合,

一般是事件激发导致委托被调用。.net中委托既可指向实例方法也可指向静态方法。

       对于委托还比较陌生,想更深入研究的同学。建议先从Lambda开始学习(工作中大部分同学接触Lambda比delegate更多,更早),再层层向内部,

反向学习匿名函数、委托。 

Delegate

      delegate是我们接触最早,最常用的一种委托。也是最灵活的一种委托,支持参数个数最多可达32个,即可支持有返回值,也支持无返回值。

      操作分三步:定义委托(生成指针,放入堆);将实例化对象和委托绑定 (委托指针与实例化对象内存地址关联上);执行委托。

      例子:

 /// <summary>
        /// 声明委托
        /// </summary>
        /// <param name="firstStr">第一个夫字符串</param>
        /// <param name="secondStr">第二个字符串</param>
        /// <returns></returns>
        public delegate string OprateString(string firstStr, string secondStr);

        /// <summary>
        /// Delegate
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_delegate_Click(object sender, EventArgs e)
        {
            DelegateOpt ctStr = new DelegateOpt();

            //指针连接对象地址
            OprateString pttStr = ctStr.ConnectString;
            
            //同步直接执行委托
            string result= pttStr.Invoke("同学:", "学习委托delegate");


            //取消绑定,再重新绑定
            pttStr-= ctStr.ConnectString;
            pttStr += ctStr.ExcludeString;
            //指针连接对象地址
            IAsyncResult iAsyncRsl = pttStr.BeginInvoke("同学:", "学习委托delegate", null,null);
            //主线程中........可在此段时间做其他的事
            //Thread.Sleep(1000);//主线程休息2秒钟
            string returnStrFromAsyncMethod = pttStr.EndInvoke(iAsyncRsl);//调用EndInvoke返回最终结果

            txt_reveal.Text = returnStrFromAsyncMethod + "\r\n"; //输出结果
        }

        /// <summary>
        /// 连接字符串
        /// </summary>
        /// <param name="firstStr"></param>
        /// <param name="secondStr"></param>
        /// <returns></returns>
        public string ConnectString(string firstStr, string secondStr)
        {
            return firstStr + secondStr;
        }

        /// <summary>
        /// firstStr 中排除 secondStr
        /// </summary>
        /// <param name="firstStr"></param>
        /// <param name="secondStr"></param>
        /// <returns></returns>
        public string ExcludeString(string firstStr, string secondStr)
        {
            return firstStr.IndexOf(secondStr) > -1 ? firstStr.Replace(secondStr, "") : firstStr;
        }
View Code

Action中用Lambda

      Action<T1,... ,Tx>  当今比较常见的一种委托 ,以参数形式传递方法,而不用显式声明自定义的委托。 封装的方法必须与此委托定义的方法签名相对应,不能有返回值(在 C# 中,该方法必

须返回 void)。相对于delegate,少了定义委托步骤,至少1个参数,至多4个参数,无返回值。

      例子:

        /// <summary>
        /// 同步获取队列消息
        /// </summary>
        /// <typeparam name="T">返回的数据类型</typeparam>
        /// <param name="action">获取数据后的回调</param>
        public void Receive<T>(Action<T> action) where T : class
        {
            try
            {
                mqueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
                Message message = mqueue.Receive();
                T obj = message.Body as T;
                if (obj != null)
                    action(obj);
                else
                    throw new InvalidCastException("队列获取的类型与泛型类型不符");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
View Code
 /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="queueName">队列</param>
        /// <param name="isAsy">是否异步</param>
        public user ReceiveMessage(string queueName, bool isAsy)
        {
            user userInfo = new user();
            MessageQueueSettings mqs = new MessageQueueSettings()
            {
                MessageQueueName = queueName
            };
            IMessageQueueBase messageQueueBase = new MessageQueueBase(mqs);
            if (!isAsy)
            {

                ///直接用lambda 获取接收信息
                messageQueueBase.Receive<user>(p =>
                {
                    userInfo = p;
                });
            }
            else
            {
                messageQueueBase.ReceiveAsync<user>(p =>
                {
                    userInfo = p;
                });
            }
            return userInfo;
        }
View Code

Func中用Lambda

      用法类似的最流行的一种委托,相对于Action多了一个可以返回信息功能,至少0个参数,至多4个参数,根据返回值泛型返回。

      例子:与Expression一起使用,直接操作lambda.

        /// <summary>
        /// 根据表达式查询数据
        /// </summary>
        /// <typeparam name="T">查询的数据类型</typeparam>
        /// <param name="express">表达式</param>
        /// <returns>查询结果</returns>
        public IList<T> FindDataByExpress(Expression<Func<T, bool>> express)
        {
            try
            {
                return collection.AsQueryable().Where(express).ToList<T>();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
View Code
        /// <summary>
        /// 列表查询测试
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public IList<users> TestSearchList(users user)
        {
            string dbName = MongoDatabase.LW_Test.GetEnumItemDescription();
            var mongoBase = new MongoBase<users>(dbName).GetDBHelper();
            var result = mongoBase.FindDataByExpress(p => p.Age == user.Age && p.Name.Contains(user.Name));
            return result;
        }
View Code

应用的场景

      1、一般在多个功能对一个功能依赖的一对多关系中用到,当一个对象改变时,其他依赖它的对象随之发生相应改变。

      2、实体操作类中用到,现在操作数据库的持久化处理程序,多数是直接数据实体映射,用Action和Func处理很方便。

      3、待补充。。。

原文地址:https://www.cnblogs.com/harveybarray/p/6654747.html