每次和人讲解C#的委托以及Lambda都比较费劲,其实概念是非常简单的,但不好描述。今天写了一个非常简单粗暴直观的例子,来给大家看一下Labmda、委托的用法和关系,顺便也演示了拓展方法、对象初始化器的使用。
我描述的是这样的场景:有3个屌丝,他们每个人都要做两件事,打魔兽和撸撸睡。
首先,我定义了一个屌丝类,描述一个屌丝对象:
class Diaos { public Guid Id { get; set; } public string Name { get; set; } }
然后,我们要创建3个屌丝,以往我们用C#来让完成这件事通常会这样写:
List<Diaos> diaoses = new List<Diaos>(); Diaos d1 = new Diaos(); d1.Id = Guid.NewGuid(); d1.Name = "Michael"; diaoses.Add(d1); Diaos d2 = new Diaos(); d2.Id = Guid.NewGuid(); d2.Name = "Edi"; diaoses.Add(d2); Diaos d3 = new Diaos(); d3.Id = Guid.NewGuid(); d3.Name = "Kasim"; diaoses.Add(d3);
显然这是一个简单的操作,但代码却如此复杂。如果使用var关键词和对象初始化器来做这件事就会截然不同:
var diaoes = new List<Diaos>() { new Diaos() { Id = Guid.NewGuid(), Name = "Michael" }, new Diaos() { Id = Guid.NewGuid(), Name = "Edi" }, new Diaos() { Id = Guid.NewGuid(), Name = "Kasim" } };
这段代码非常清晰易懂。var的意思不是没有类型或者弱类型,而是让编译器在编译时推断类型。在编程时只是节约了我们的代码量,特别是碰到名字非常长的对象时,var可以省去我们很多键入操作。对象初始化器的语法也很简单,在new一个对象的时候,后面加上一对花括号,就可以在花括号里给对象的属性赋值。
现在我们有了3个屌丝,怎么样让他们先打魔兽再撸撸睡呢?以前我们会这样写:
private static void PlayWOW(Diaos diaos) { Console.WriteLine("{0} played WOW with his gay friend.", diaos.Name); } private static void LuLuSleep(Diaos diaos) { Console.WriteLine("{0} lued one shot and went to sleep.", diaos.Name); } ... foreach (var item in diaoes) { PlayWOW(item); LuLuSleep(item); }
然而,我们认为"打魔兽"和"撸撸睡"是属于屌丝本身的操作,如果可以直接“.”出来不是更自然吗?那就改用拓展方法吧!拓展方法要放在一个静态类里,所以拓展方法自然也都是静态方法,被拓展的类型前面加this就好了,就像这样:
static class DiaosExtensions { public static void PlayWOW(this Diaos diaos) { Console.WriteLine("{0} played WOW with his gay friend.", diaos.Name); } public static void LuLuSleep(this Diaos diaos) { Console.WriteLine("{0} lued one shot and went to sleep.", diaos.Name); } }
现在我们就可以把那段foreach循环改成:
foreach (var item in diaoes) { item.PlayWOW(); item.LuLuSleep(); }
这段代码看上去更加自然和牛逼。如果我们要进一步装逼,我们可以把这段foreach代码也省了,改用List的ForEach()拓展方法,往里面传一个委托。听晕了?其实很简单:
diaoes.ForEach(delegate(Diaos d) { d.PlayWOW(); d.LuLuSleep(); });
这里的delegate是对被ForEach的对象(即Diaos对象)进行的一些列的操作,所以delegate的类型一定要是Diaos。一般我们会在代码的别的地方定义一个方法体(比较主流的做法),但是直接写在delegate里面也是可以的(它原本就是这样用的)。
我本人认为delegate是一种多态的表现,它可以像传参数一样,传一个方法,这个方法自然是可变的。
如果你都懒的写delegate这些字,那就用lambda表达式,lambda只是delegate的语法糖:
diaoes.ForEach(p => { p.PlayWOW(); p.LuLuSleep(); });
“p”只是一个“临时变量”,可以看作占位符,它代表lambda表达式里处理的对象,这里就是Diaos类型的对象。
如果你只想做一个操作,那么大括号也可以省略:
diaoes.ForEach(p => p.PlayWOW());
最后,整个例子的完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LambdaTest { class Diaos { public Guid Id { get; set; } public string Name { get; set; } } static class DiaosExtensions { public static void PlayWOW(this Diaos diaos) { Console.WriteLine("{0} played WOW with his gay friend.", diaos.Name); } public static void LuLuSleep(this Diaos diaos) { Console.WriteLine("{0} lued one shot and went to sleep.", diaos.Name); } } class Program { static void Main(string[] args) { var diaoes = new List<Diaos>() { new Diaos() { Id = Guid.NewGuid(), Name = "Michael" }, new Diaos() { Id = Guid.NewGuid(), Name = "Edi" }, new Diaos() { Id = Guid.NewGuid(), Name = "Kasim" } }; foreach (var item in diaoes) { item.PlayWOW(); item.LuLuSleep(); } //diaoes.ForEach(p => p.PlayWOW()); diaoes.ForEach(p => { p.PlayWOW(); p.LuLuSleep(); }); //diaoes.ForEach(delegate(Diaos d) { d.PlayWOW(); d.LuLuSleep(); }); } } }
运行结果:
-0-