Lambda表达式

不知道你有没有听说过函数式编程语言(例如Lips),在函数式编程语言里所有的参数和返回值类型都是函数,根本也就没有变量的概念。但是函数式编程语言很明显没有得到广泛的推广,我们这里不讨论函数式编程的好与坏,但是它里面的一些思想对我们还是很有帮助的。说了那么多好像是在谈函数式编程一样,步入正题吧!

在.net1.1版本中我们已经有了类似于函数指针的东西,也就是委托,他可以让函数以参数的形式进行传递。但是很明显我们并没有实现函数式编程。在.net2.0中的匿名方法似乎使这种状况得到了改变,但事实上到了.net3.0的Lambda我们才真正做到了函数式编程。我们看看这三个版本的委托形式变化吧:

.net1.1中:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Lambda { class Program { public delegate void MyDelegate(string msg); public static void MyMethod(string msg) { Console.WriteLine(msg); } static void Main(string[] args) { MyDelegate md = new MyDelegate(MyMethod); md("Hello World!"); } } }

.net2.0中:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Lambda { class Program { public delegate void MyDelegate(string msg); public static void MyMethod(string msg) { Console.WriteLine(msg); } static void Main(string[] args) { MyDelegate md = MyMethod; md("Hello World!"); } } }

这样的写法或许用途不大,我们来看看2.0中的新增“匿名方法”:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Lambda { class Program { public delegate void MyDelegate(string msg); static void Main(string[] args) { MyDelegate md = delegate(string msg) { Console.WriteLine(msg); }; md("hello world!"); } } }

但是这个例子不明显,我们再看一个例子,首先你是否经常看到这样的代码:

private void frmTest_Load(object sender, EventArgs e) { btnShowMessage.Click += new EventHandler(btnShowMessage_Click); } void btnShowMessage_Click(object sender, EventArgs e) { MessageBox.Show("Hello World!"); }

我在From的Load事件中添加一个事件(其实也是委托),然后事件要执行的代码我们单独放到一个方法中。其实类似的又很多,例如在异步调用中需要Callback函数等,我们都需要单独的写成一个方法。这种方法对于上面这个例子来说主要是方法体本身比方法声明还小没有必要单独放到一个方法中,而如果我们遇到需要访问frmTest_Load的参数或其内部变量的话是不是就比较麻烦了???对了我们看看“匿名方法” 如何避免这类问题:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace AnoymousMethod { public partial class frmTest : Form { public frmTest() { InitializeComponent(); } private void frmTest_Load(object sender, EventArgs e) { btnShowMessage.Click += delegate(object sender2, EventArgs e2) { MessageBox.Show("Hello World!"); }; } } }

看看上面的代码是不是就不会出现上述提到的问题了,匿名方法内联在方法内部形成了一个闭包。但是这样的写法或许还不够简练整洁,那么我们接着看在.net3.0中用Lambda表达式怎么去做(╮(╯▽╰)╭终于到主题了):

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Lambda { class Program { public delegate void MyDelegate(string msg); static void Main(string[] args) { MyDelegate md = msg => Console.WriteLine(msg); md("hello world!"); } } }

这样的方法不知道给你什么感觉,反正我第一次看到的时候唯一的感觉就是.net太伟大了!当然了,对于这个例子来说我们的功能太弱了,但是别着急,我们先来分析一下这样的表达,再来看看较复杂的例子。

“msg=>Console.WriteLine(msg)”,这句话的意思就是给你一条信息,你给我输出,表达很简单,我们来看看表达式形式。

Lambda的形式是"arg=>body ",左边是参数列表,多个参数用","分开;右边是表达式主体;“=>”读作“goes to”。简单吧,事实上我们的Lambda表达式最终还是编译成了匿名方法,但是它的表达方式是如此的优雅!好了,我们一块进一步学习一下Lambda表达式。还是直接看代码,而且注释都很清楚:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Lambda { class User { public User() { } public User(int id, string name) { this.Id = id; this.Name = name; } public int Id { get; set; } public string Name { get; set; } } class MyClass { delegate bool Mydelegate(int a, int b); public static void Main(string[] args) { //Lambda表达式 (input parameters)=>expression Mydelegate md1 = (x, y) => x == y; Console.WriteLine(md1(1, 2)); //Lambda语句 (input parameters)=>{statement;} Mydelegate md2 = (x, y) => { x++;return x == y; }; Console.WriteLine(md2(1, 2)); //Lambda中的泛型委托 //如果我们简单实用上面的操作就显得功能有些有限了,而且我们在Main行数外 //还要定义Mydelegate这个委托。事实上我们还有更强的的功能: Func<int, int, bool> func1 = (x, y) => x == y; Console.WriteLine(func1(1, 2)); //我相信通过上面的例子您已经看到了Func这个泛型委托的作用了(最后一个参数是返回类型, //前面是参数类型,而且lambda表达式参数类型会自动根据前面泛型参数类型进行判断), //至少不需要再定义一个委托来专门用来使用lambda(当然还有别的很多功能请继续看)。 //带有标准查询运算符的Lambda int[] myArray = new int[] { 2,5,7,2,8,9,1}; int oddNumber=myArray.Count(i=>i%2==1);//参数是Lambda表达式 Console.WriteLine(oddNumber); //Lambda类型推理 List<User> users = new List<User>(); users.Add(new User(1,"cmj")); users.Add(new User() { Id = 2, Name = "smm" }); User us= users.Where(x => x.Name == "smm").First(); Console.WriteLine(us.Id); //我们发现,在where的参数中并没有指明x是什么类型,但是我们却能够正确的查询,这就是Lambda //的类型推理的结果。另外,我们发现这样一种查询是如此的优雅,让我想起周星驰拍摄长江七号时 //在给七仔的设计人员描述七仔时说“我要一只狗,一只外星狗。”,看一下我们的查询“我要一个User, //一个name为smm的User”。再者用过Linq的一定很熟悉这种查询,其实Linq正是使用这种查询方法。 } } }

相信看了上面的例子我们对Lambda表达式已经有了初步了解,我们再来看一些应用吧。

先看看下面的代码:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace MultiThreadInvokeWinform { public partial class frmTest : Form { public frmTest() { InitializeComponent(); } delegate string MyDelegateForAync(); static string CreateLabel() { for (int i = 0; i < 999999; ++i) { //做一些操作 } return "OK"; } MyDelegateForAync md; private void btnStart_Click(object sender, EventArgs e) { md = new MyDelegateForAync(CreateLabel); md.BeginInvoke(new AsyncCallback(AsyncCallbackForSetInfo), null); } void AsyncCallbackForSetInfo(IAsyncResult iaResult) { string str= md.EndInvoke(iaResult); SetInfo(str); } void SetInfo(string str) { lbInfo.Text = str; } } }

代码作用就是为了使异步调用成功后给界面上的Label一个提示信息,相信这种操作经常用吧,但是如果做个winfrom多线程开发的朋友肯定会发现问题,这个代码会报错:线程间操作无效: 从不是创建控件“lbInfo”的线程访问它。就是说你在异步调用的进程中访问winform的线程是不安全的。我们解决这个问题就要利用invoke方法,多定义一个委托:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace MultiThreadInvokeWinform { public partial class frmTest : Form { public frmTest() { InitializeComponent(); } delegate string MyDelegateForAync(); delegate void MyDelegateForInvoke(string str); static string CreateLabel() { for (int i = 0; i < 999999; ++i) { //做一些操作 } return "OK"; } MyDelegateForAync md; private void btnStart_Click(object sender, EventArgs e) { md = new MyDelegateForAync(CreateLabel); md.BeginInvoke(new AsyncCallback(AsyncCallbackForSetInfo), null); } void AsyncCallbackForSetInfo(IAsyncResult iaResult) { string str= md.EndInvoke(iaResult); //SetInfo(str); MyDelegateForInvoke md2 = new MyDelegateForInvoke(SetInfo); this.Invoke(md2, str); } void SetInfo(string str) { lbInfo.Text = str; } } }

上面这种做法很普遍,但是为了这就要多定义一个委托,有点那个了吧。。。我们用匿名方法解决:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace MultiThreadInvokeWinform { public partial class frmTest : Form { public frmTest() { InitializeComponent(); } delegate string MyDelegateForAync(); static string CreateLabel() { for (int i = 0; i < 999999; ++i) { //做一些操作 } return "OK"; } MyDelegateForAync md; private void btnStart_Click(object sender, EventArgs e) { md = new MyDelegateForAync(CreateLabel); md.BeginInvoke(new AsyncCallback(AsyncCallbackForSetInfo), null); } void AsyncCallbackForSetInfo(IAsyncResult iaResult) { string str= md.EndInvoke(iaResult); //SetInfo(str); this.Invoke((MethodInvoker)delegate() { SetInfo(str); }); } void SetInfo(string str) { lbInfo.Text = str; } } }

还麻烦?我们用Lambda表达式:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace MultiThreadInvokeWinform { public partial class frmTest : Form { public frmTest() { InitializeComponent(); } delegate string MyDelegateForAync(); static string CreateLabel() { for (int i = 0; i < 999999; ++i) { //做一些操作 } return "OK"; } MyDelegateForAync md; private void btnStart_Click(object sender, EventArgs e) { md = new MyDelegateForAync(CreateLabel); md.BeginInvoke(new AsyncCallback(AsyncCallbackForSetInfo), null); } void AsyncCallbackForSetInfo(IAsyncResult iaResult) { string str= md.EndInvoke(iaResult); //SetInfo(str); this.Invoke((MethodInvoker)(()=>SetInfo(str))); } void SetInfo(string str) { lbInfo.Text = str; } } }

当然了,还有很多的例子我们都可以用到Lambda表达式和匿名方法,这里就不再一一列举了(注意上面我们的委托MyDelegateForAync也是可以简化的,可以通过C#泛型委托Func<>来直接使用,而不需要声明。另外上面的SetInfo方法我们也可以不要,直接在Lambda中写成()=>lbInfo.Text=str。我就不再写了,自己可以试试吧)!

 

你可能感兴趣的:(Lambda表达式)