WPF-MVVM模式学习笔记4——Lambda表达式学习

在学习MVVM的过程中,其中自定义了一个超类NotificationObject,如下

    public abstract class NotificationObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void RaisePropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null) throw new ArgumentNullException("propertyNames");

            foreach (var name in propertyNames)
            {
                this.RaisePropertyChanged(name);
            }
        }

        protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
        {
            var propertyName = ExtractPropertyName(propertyExpression);
            this.RaisePropertyChanged(propertyName);
        }

        public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
        {
            if (propertyExpression == null)
            {
                throw new ArgumentNullException("propertyExpression");
            }

            var memberExpression = propertyExpression.Body as MemberExpression;
            if (memberExpression == null)
            {
                throw new ArgumentException("PropertySupport_NotMemberAccessExpression_Exception", "propertyExpression");
            }

            var property = memberExpression.Member as PropertyInfo;
            if (property == null)
            {
                throw new ArgumentException("PropertySupport_ExpressionNotProperty_Exception", "propertyExpression");
            }

            var getMethod = property.GetGetMethod(true);
            if (getMethod.IsStatic)
            {
                throw new ArgumentException("PropertySupport_StaticExpression_Exception", "propertyExpression");
            }

            return memberExpression.Member.Name;
        }

    }

其中有一个函数 protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression),这里有个Expression<Func<T>我不是很懂,后来查查是lambda表达式,好吧,说实话,我看到这个Lambda有点蛋疼菊紧...之前碰到过,没有深究,这次再试着去学习理解一下,在网上看了很多资料,其中有一位小伙伴讲的不错的,摘抄下来,哈哈。(我建议直接先去看原文,然后再返回来看我的而文章,因为我下面的例子是基于WPF讲解的,并且像一些Button的命令绑定都贱贱的用上了,担心看的人再看蒙圈了,点此阅读原文

lambda简介

lambda运算符简化了匿名委托的使用,使代码更加简洁、优雅,据说它是微软自C#1.0后新增的最重要的功能之一。

lambda运算符:所有的lambda表达式都是用新的lambda运算符“ => ”,可以称呼这个运算符为“转到”或者“成为”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体。

lambda表达式:

1. 一个参数: param => expression

2. 多个参数: (paramlist) => expression

上面这些东西,先记着,看完下面的再反过来看,理解更深刻。

lambda有啥好?

根据一个demo,慢慢进行分析,例子如下

namespace LambdaDemo.ViewModel
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class VMLambdaStudyDemo1
    {
        public static List<Person> PersonsList()
        {
            List<Person> persons = new List<Person>();

            for (int i = 0; i < 7; i++)
            {
                Person p = new Person() {Name = i + "儿子",Age = 8-i};
                persons.Add(p);
            }
            return persons;
        }
        /// <summary>
        /// Btn_LambdaDemo1 Click事件
        /// </summary>
        public void Btn_LambdaDemo1_Click()
        {
            List<Person> persons = PersonsList();

            persons = persons.Where(p => p.Age > 5).ToList(); //所有Age>5的Person的集合

        }
    }

}

上面的Demo工程为按下按钮Btn_LambdaDemo1后,只保留persons集合中的Age大于5的元素。这样直接看,对于我这个新手不明显,我还是喜欢打断点追踪,如下图

我在33行和35行、37行各打一个断点,运行程序

点击按钮Btn_LambdaDemo1,程序停留在第一个断点处

可以看到此时persons集合中元素为空,紧接着 F5 运行到下一个断点

此时我们发现,集合persons中已经添加了7个元素,接着F5运行到下一个断点

这个时候就会发现,集合persons中只剩下3个元素,这3个元素均满足我们的筛选条件。

到这里,可以看出咱们的这个lambda表达式 p => p.Age > 5  起作用了,但是这还不能看出lambda的好处,大家也知道,好是相对的嘛,那咱就不用lambda实现这个筛选功能,换另一种方式

如上图,我将35行屏蔽,用37行-46行代码实现这个筛选功能,你可以打断点追踪一下,最后也能实现我们的要求。到这里,上面两种方式两两比较,咱们就能像张同学说的那样“lambda确实是一个甜枣”。(这个工程比较简单,如有需要,可自行下载,点此下载

lambda确实挺好

上面的例子中,我用一种普通的方法来与lambda比较,来说明lambda的好处,但其实理论上不应该这样比较的。因为我在前面lambda简介中提到lambda简化了匿名委托的应用,使代码更加简洁,所以理论上我应该用匿名委托的方式来衬托一下lambda的好。下面就看一下 (p=>p.Age>5)这样的表达式到底是怎么回事,我将上面的代码修改为下

namespace LambdaDemo.ViewModel
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class VMLambdaStudyDemo1
    {
        //委托
        delegate List<Person> UNamedWeiTuo(List<Person> persons);

        public static List<Person> PersonsList()
        {
            List<Person> persons = new List<Person>();

            for (int i = 0; i < 7; i++)
            {
                Person p = new Person() {Name = i + "儿子",Age = 8-i};
                persons.Add(p);
            }
            return persons;
        }

        public void Btn_LambdaDemo1_Click()
        {
            List<Person> persons = PersonsList();

            UNamedWeiTuo uwt = WeiTuoFunc;

            persons = uwt(persons);

        }

        //委托方法
        public static List<Person> WeiTuoFunc(List<Person> persons)
        {
            List<Person> personstmp = new List<Person>();
            foreach (Person p in persons)
            {
                if (p.Age > 5)
                {
                    personstmp.Add(p);
                }
            }

            return personstmp;
        }
    }

}

在上面的代码中,我定义了一个委托UNamedWeiTuo(关于委托我就不多说了哈,请自行脑补),利用委托方法实现我们对集合persons的删选的要求。将上述的委托与lambda表达式 (p=>p.Age>5)一比较,我们就很可以看出来表达式的优点。同时也可以看出:其实表达式 (p=>p.Age>6) 中的 p 就代表委托方法中的参数,而表达式符号右边的

p.Age>5 就代表委托方法的返回结果。

lambda趁热打铁

下面就直接上张同学的更加直观的例子:

1.单个参数

使用委托方法

    public class VMLambdaStudyDemo1
    {
        //委托  逛超市
        delegate int GuangChaoShi(int a);
        public void Btn_LambdaDemo1_Click()
        {
            GuangChaoShi gwl = JieZhang;
            MessageBox.Show(gwl(10) + ""); //打印结果为 "20"

        }

        public static int JieZhang(int a)
        {
            return (a + 10);
        }
    }

使用lambda表达式

    public class VMLambdaStudyDemo1
    {
        //委托  逛超市
        delegate int GuangChaoShi(int a);
        public void Btn_LambdaDemo1_Click()
        {
            GuangChaoShi gwl = p => p + 10;
            MessageBox.Show(gwl(10) + ""); //打印结果为 "20"

        }

    }

Oh My LadyGaGa,That‘s greatful!,下面再来个多参数的,但是委托方法内部运算比较简单的。

2.多参数,主体运算简单

使用委托方法

    public class VMLambdaStudyDemo1
    {
        //委托  逛超市
        delegate int GuangChaoShi(int a,int b);
        public void Btn_LambdaDemo1_Click()
        {
            GuangChaoShi gwl = JieZhang;
            MessageBox.Show(gwl(10,100) + ""); //打印结果为 "80"

        }

        //结账
        public static int JieZhang(int a, int b )
        {
            return (b - (a + 10));
        }
    }

使用lambda表达式

    public class VMLambdaStudyDemo1
    {
        //委托  逛超市
        delegate int GuangChaoShi(int a,int b);
        public void Btn_LambdaDemo1_Click()
        {
            GuangChaoShi gwl = (p,z) => z - (p + 10);
            MessageBox.Show(gwl(10,100) + ""); //打印结果为 "80"

        }

    }

不错不错,下面还是来个多参数的,但是委托方法内部运算较为复杂的

3.多参数,主题运算复杂

使用委托方法

    public class VMLambdaStudyDemo1
    {

        /// <summary>
        ///委托  逛超市
        /// </summary>
        /// <param name="a">花费</param>
        /// <param name="b">付钱</param>
        /// <returns>找零</returns>
        delegate int GuangChaoShi(int a,int b);
        public void Btn_LambdaDemo1_Click()
        {
            GuangChaoShi gwl = JieZhang;
            MessageBox.Show(gwl(20,100) + ""); //打印结果为 "70"

        }

        //结账
        public static int JieZhang(int a, int b )
        {
            int zuidixiaofei = 10;
            if (b < zuidixiaofei)
            {
                return 100;
            }
            else
            {
                return (b - a - zuidixiaofei);
            }

        }
    }

使用lambda表达式

    public class VMLambdaStudyDemo1
    {

        /// <summary>
        ///委托  逛超市
        /// </summary>
        /// <param name="a">花费</param>
        /// <param name="b">付钱</param>
        /// <returns>找零</returns>
        delegate int GuangChaoShi(int a,int b);
        public void Btn_LambdaDemo1_Click()
        {
            GuangChaoShi gwl = (p, z) =>
                {
                    int zuidixiaofei = 10;
                    if (p < zuidixiaofei)
                    {
                        return 100;
                    }
                    else
                    {
                        return (z - p - zuidixiaofei);
                    }
                };

            MessageBox.Show(gwl(20,100) + ""); //打印结果为 "70"

        }

    }

不错不错,写到这里,我对lambda表达式突然开窍了。

上面这些例子,好好理解下,下面我继续摘抄张同学的,介绍一个系统指定的Func<T>委托。

Func<T>委托

T 是泛型的参数类型,这是一个泛型类型的委托,用起来比较方便

先上例子

    public class VMLambdaStudyDemo1
    {

        public void Btn_LambdaDemo1_Click()
        {
            Func<int, string> gwl = p => p + 10 + "--返回类型为string类型";
            MessageBox.Show(gwl(10)); //打印结果为 "20--返回类型为string类型"

        }

    }

说明,这里可以看到,p为int类型参数,然而lambda主体返回的是string类型的,你看我的MessageBox.Show()里面都不用加 “” 了。

我勒个去,好简单的说。

再上一个例子

        public void Btn_LambdaDemo1_Click()
        {
            Func<int, int, bool> gwl = (p, j) =>
                {
                    if ((p + j) == 10)
                    {
                        return true;
                    }
                    return false;
                };
            MessageBox.Show(gwl(5,5) + ""); //打印结果为 "True"

        }

说明:从这个例子,我们能看到,p为int类型,j为int类型,返回值为bool类型。

看完上面的两个例子,相信大家应该明白Func<T>的用法:多个参数,前面的为委托方法的参数,最后一个参数为委托方法的返回类型。不得不说,这个张同学的思路讲的真清晰啊!

lambda表达式总结

① “lambda表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托。

② lambda表达式格式为:(参数列表) => 表达式或语句块

③ 说白了,lambda表达式和匿名方法其实是一件事,它们的作用都是:产生方法。即内联方法。

④  lambda不仅可用于创建委托,还可以用于创建表达式目录树。下一小节就讲lambda表达式树!

lambda表达式树

注意上面标题是“lambda表达式树”,而不是“lambda表达式”!

● Expressions Tree概念

① 表达式树又称为表达式目录树,以数据形式表示语言级代码。所有的数据都存储在树结构中,每个节点表示一个表达式。

② Lambda表达式树允许我们像处理数据(比如读取,修改)一样来处理 lambda表达式。(注意这里可以看出lambda表达式树和lambda表达式的关系!)

● 表达式的种类(这里可以先了解一下,随着实验的深入返过来再看理解的会更深)

① 参数表达式:ParameterExpression. 就是一个方法中的参数,例如   search(string key)中的key可以看成是一个参数表达式;再例如lambda表达式中 (n => n * 3)中的n也可以看成是一个参数表达式。

② 二次元表达式:BinaryExpression. 例如 a + b 或者 (n * 3) < 5 等等。

③ 方法调用表达式:MethodCallExpression,例如自定义LINQ提供程序中实现Orderby的操作。

④ 常数表达式:ConstantExpression. 例如数值5

⑤ 带有条件运算的表达式:ConditionExpression

还有很多,可以去看MSDN,不解释。

下面举个简单的例子,还是采用断点的方式追踪

        public void Btn_LambdaDemo1_Click()
        {
            Func<int, bool> test = n => (n * 3) < 5;

            Expression<Func<int, bool>> filter = n => (n * 3) < 5;        

        }

从程序可以看出,test和filter均用到的 lambda表达式 n => (n * 3) < 5,但是 filter 利用Expression将lambda表达式以目录树的形式表示为数据结构,下面咱们运行程序,看看这两个到底有什么不同。

程序运行到第一个断点处

此时 test 和 filter均为空,两次F5,程序运行到第3个断点处,展开test和filter,如下图

从上图中可以看到,两者的内容大不一样,其中filter里面包含了很多 lambda表达式 (n => (n*3)<5) 的信息,比如Body主体是 (n*3)<5,返回类型ReturnType为Boolen类型,有1个参数。展开filter的Body,如下图

可以看到Body里面还可以看到主体左边为 n * 3 ,主体右边为 5,节点类型为 < 即LessThan等等,信息量好大的说。

下面通过函数直观的展现表达式树的内部信息

    public class VMLambdaStudyDemo1
    {

        public void Btn_LambdaDemo1_Click()
        {
            Func<int, bool> test = n => (n * 3) < 5;

            Expression<Func<int, bool>> filter = n => (n * 3) < 5;
            BinaryExpression lt = (BinaryExpression)filter.Body;
            BinaryExpression mult = (BinaryExpression)lt.Left;
            ParameterExpression en = (ParameterExpression)mult.Left;
            ConstantExpression three = (ConstantExpression)mult.Right;
            ConstantExpression five = (ConstantExpression)lt.Right;
            var One = filter.Compile();
            MessageBox.Show(One(5) + " || " + One(1) + " || "
                        + lt.NodeType  + " || " + mult.NodeType
                         + " || " + en.Name + " || " +
                         three.Value + " || " + five.Value);

        }

    }

可以自己断点追踪一下看看,我就不演示了,截图好麻烦的说。下面是程序的运行结果

上面的例子可以直观的展示 “ Lambda表达式树允许我们像处理数据(比如读取,修改)一样来处理 lambda表达式”这一句话,再来个例子,加深印象。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace LambdaDemo.ViewModel
{

    public class VMLambdaStudyDemo1
    {

        public void Btn_LambdaDemo1_Click()
        {
            ParameterExpression a = Expression.Parameter(typeof(int), "i"); //创建一个表达式树中的参数,作为一个节点,这里是最下层的节点
            ParameterExpression b = Expression.Parameter(typeof(int), "j");
            BinaryExpression be = Expression.Multiply(a, b);  //这里i*j,生成表达式树中的一个节点,比上面节点高一级

            ParameterExpression c = Expression.Parameter(typeof(int), "w");
            ParameterExpression d = Expression.Parameter(typeof(int), "x");
            BinaryExpression be1 = Expression.Multiply(c, d);

            BinaryExpression su = Expression.Add(be, be1);   //运算两个中级节点,产生终结点

            Expression<Func<int, int, int, int, int>> lambda = Expression.Lambda<Func<int, int, int, int, int>>(su, a, b, c, d);

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

            System.Windows.MessageBox.Show("lambda:" + lambda + "Result:" + f(1, 1, 1, 1));
        }

    }

}

运行结果为

上段代码的lambda表达式树的结构图,直接用张同学的了

结合这张图再看代码,仔细理解下,应该能理解的比较透彻了。

结语

我勒个去,敲了这么长,比敲代码累多了,敲到这里,我对lambda算是比较理解了,但是反思一下,会用吗?答曰:不会!

不过没事儿,边学边用,下一篇准备研究一下 NotificationObject里的  protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)

时间: 2024-10-10 07:32:02

WPF-MVVM模式学习笔记4——Lambda表达式学习的相关文章

Java8新特性学习笔记(一) Lambda表达式

没有用Lambda表达式的写法:  Comparator<Transaction> byYear = new Comparator<Transaction>() {             @Override            public int compare(Transaction o1, Transaction o2) {                return o1.getValue().compareTo(o2.getValue());             

Struts2学习笔记(OGNL表达式)

Struts2学习笔记(OGNL表达式) Struts 2支持以下几种表达式语言: OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言: JSTL(JSP Standard Tag Library),JSP 2.0集成的标准的表达式语言: Groovy,基于Java平台的动态语言,它具有时下比较流行的动态语言(如Python.Ruby和Smarttalk等)的一些起特性: Velocity,严格来说不是表达式语言,它是一种基于Ja

mongodb 学习笔记 03 -- 查询表达式

mongodb 学习笔记 03 – 查询表达式 不等于,大于,小于 !=: db.stu.find({name:{$ne:'billvsme'}}) 名字不是'billvsme' > : db.stu.find({age:{$gt:18}}) 年纪大于18 < : db.stu.find({age:{$lt:18}}) 年纪小于18 >=: $gte <=: $lte in/not in/all $in :db.goods.find(stu_id:{$in:[93001,93002

WPF MVVM模式的一些理解

/*本文转自 http://www.cnblogs.com/sirkevin/archive/2012/11/28/2793471.html */ 使用WPF+Mvvm开发一年多,期间由于对Mvvm模式的理解不足,遇到了很多问题,也绕了很多弯子:网上提供的Mvvm的示例比较简单,实际项目中的需求也各种各样.不过经过几个项目,也有了一些对Mvvm模式的理解: 1. Mvvm是什么,Mvvm是怎么来的?Mvvm模式广泛应用在WPF项目开发中,使用此模式可以把UI和业务逻辑分离开,使UI设计人员和业务

WPF MVVM模式

1. MVVM MVVM的设计模式最早于2005年由微软的WPF和Silverlight架构师John Gossman在他的博客中提到. WPF中采用MVVM的架构可以获得以下好处: 1. 将UI和业务的设计完全分开,View只是ViewModel的消费者 2. 有助于我们区别并哪些是UI操作,哪些是业务操作,而不是将他们混淆 3.层与层之间耦合度降低,这一点非常符合面向对象(OOP)的思想. 2.MVVM 用图来表示,这个是从网上找的图,简单明了,省去了自己画.   3.下面来一步一步写代码吧

WPF MVVM模式下动画的实现

原文:WPF MVVM模式下动画的实现 在MVVM模式下,数据的显示都是通过绑定来实现的.当我们在ViewModel里修改数据时,View里面的界面会瞬间变化.但是如果我们希望这个变化有一个动画效果,应该怎么做呢? 可能一开始我们会想到DoubleAnimation.StoryBoard这些东西,但我们很快就会发现,它们只能操作View里面的元素,我们无法在ViewModel里使用它们. 我们在这里使用的方法是:创建一个类似DoubleAnimation的类,它的操作对象就是普通的double类

HANA学习笔记1-搭建HANA学习环境

一 硬件环境     两台电脑,一台为服务器装跑HANA虚拟机,一台为客户端运行HANA_STUDIO     服务器:内存至少需要16G     windows server 2003 64位     客户端:windows 7 64位   二软件环境     服务器:VMware 9.0    HANA虚拟机镜像     客户端:jdk   sapcar   SAP_HANA_CLIENT SAP_HANA_STUDIO 软件的下载地址如下:     HANA虚拟机镜像:http://pan

【web开发学习笔记】Structs2 Action学习笔记(一)

1.org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter准备和执行 2. <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> url-pattern约定熟成只写/*,没必要写*.action 3. <

【web开发学习笔记】Structs2 Action学习笔记(二)

action学习笔记2-有关于action method的讨论 Action执行的时候并不一定要执行execute方法,可以在配置文件中配置Action的时候用method=来指定执行哪个方法 也可以在url地址中动态指定(动态方法调用DMI)(推荐) 方法一 <struts> <constant name="struts.devMode" value="true" /> <package name="user" e