对于拉姆达,许多文章都讲过原理及如何使用,所以这篇文章我主要是摘录我学习过的文字,总结下我自己的学习心得。

什么是拉姆达表达式

  "Lambda表达式"是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量。它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to"。Lambda运算符的左边是输入参数(如果有),右边是表达式或语句块。

Lambda表达式Lambda表达式是由.NET 2.0演化而来的,也是LINQ的基础,熟练地掌握Lambda表达式能够快速地上手LINQ应用开发。

Lambda表达式在一定程度上就是匿名方法的另一种表现形式

//匿名方法 

IEnumerable<People> results = people.Where

(delegate(People p) { return p.age > ; });

//拉姆达表达式

IEnumerable<People> results = people.Where(People => People.age > );

使用示例

(x, y) => x * y         //多参数,隐式类型=> 表达式 

x => x *               //单参数, 隐式类型=>表达式 

x => { return x * ; }      //单参数,隐式类型=>语句块 

(int x) => x *             //单参数,显式类型=>表达式 

(int x) => { return x * ; }      //单参数,显式类型=>语句块 

() => Console.WriteLine()   //无参数

以上述格式都是Lambda表达式的合法格式,在编写Lambda表达式时,可以忽略参数的类型,因为编译器能够根据上下文直接推断参数的类型。

Lambda的进阶用法

一、闭包

var a = ;
Func<int,int> multiplyWith = x => x * a;
var result1 = multiplyWith(); //
a = ;
var result2 = multiplyWith(); //

我们可以在Lambda表达式中用到外面的变量,没错,也就是传说中的闭包啦。

void DoSomeStuff()
{
var coeff = ;
Func<int,int> compute = x => coeff * x;
Action modifier = () =>
{
coeff = ;
};
var result1 = DoMoreStuff(compute);
ModifyStuff(modifier);
var result2 = DoMoreStuff(compute);
}
int DoMoreStuff(Func<int,int> computer)
{
return computer();
}
void ModifyStuff(Action modifier)
{
modifier();
}

  在上面的代码中,DoSomeStuff方法里面的变量coeff实际是由外部方法ModifyStuff修改的,也就是说ModifyStuff这个方法拥有了访问DoSomeStuff里面一个局部变量的能力。它是如何做到的?我们马上会说的J。当然,这个变量作用域的问题也是在使用闭包时应该注意的地方,稍有不慎就有可能会引发你想不到的后果。看看下面这个你就知道了。

var buttons = new Button[];
for (var i = ; i < buttons.Length; i++)
{
var button = new Button();
button.Text = (i + ) + ". Button - Click for Index!";
button.OnClick += (s, e) => { Messagebox.Show(i.ToString()); };
buttons[i] = button;
}

猜猜你点击这些按钮的结果是什么?是”1, 2, 3…”。但是,其实真正的结果是全部都显示10。为什么?不明觉历了吧?那么如果避免这种情况呢?

var button = new Button();
var index = i;
button.Text = (i + ) + ". Button - Click for Index!";
button.OnClick += (s, e) => { Messagebox.Show(index.ToString()); };
buttons[i] = button;

其实做法很简单,就是在for的循环里面把当前的i保存下来,那么每一个表达式里面存储的值就不一样了。

  当我们的Lambda表达式里面用到了外部变量的时候,编译器会为这个Lambda生成一个类,在这个类中包含了我们表达式方法。在使用这个Lambda表达式的地方呢,实际上是new了这个类的一个实例进行调用。这样的话,我们表达式里面的外部变量,也就是上面代码中用到的local实际上是以一个全局变量的身份存在于这个实例中的。

二、表达式Expression

我们可以用一个Expression将一个Lambda保存起来。并且允许我们在运行时去解释这个Lambda表达式。来看一下下面简单的代码:

Expression<Func<MyModel, int>> expr = model => model.MyProperty;
var member = expr.Body as MemberExpression;
var propertyName = member.Expression.Member.Name;

  这个的确是Expression最简单的用法之一,我们用expr存储了后面的表达式。编译器会为我们生成表达式树,在表达式树中包括了一个元数据像参数的类型,名称还有方法体等等。在LINQ TO SQL中就是通过这种方法将我们设置的条件通过where扩展方法传递给后面的LINQ Provider进行解释的,而LINQ Provider解释的过程实际上就是将表达式树转换成SQL语句的过程。

用Lambda表达式实现一些在JavaScript中流行的模式

一、返回方法

我们在JavaScript中可以直接return一个方法,在.net中虽然不能直接返回方法,但是我们可以返回一个表达式。

Func<string, string> SayMyName(string language)
{
switch(language.ToLower())
{
case "fr":
return name => {
return "Je m'appelle " + name + ".";
};
case "de":
return name => {
return "Mein Name ist " + name + ".";
};
default:
return name => {
return "My name is " + name + ".";
};
}
}
void Main()
{
var lang = "de";
//Get language - e.g. by current OS settings
var smn = SayMyName(lang);
var name = Console.ReadLine();
var sentence = smn(name);
Console.WriteLine(sentence);
}

是不是有一种策略模式的感觉?这还不够完美,这一堆的switch case看着就心烦,让我们用Dictionary<TKey,TValue>来简化它。来看看来面这货:

static class Translations
{
static readonly Dictionary<string, Func<string, string>> smnFunctions = new Dictionary<string, Func<string, string>>();
static Translations()
{
smnFunctions.Add("fr", name => "Je m'appelle " + name + ".");
smnFunctions.Add("de", name => "Mein Name ist " + name + ".");
smnFunctions.Add("en", name => "My name is " + name + ".");
}
public static Func<string, string> GetSayMyName(string language)
{
//Check if the language is available has been omitted on purpose
return smnFunctions[language];
}
}

二、自定义型方法

自定义型方法在JavaScript中比较常见,主要实现思路是这个方法被设置成一个属性。在给这个属性附值,甚至执行过程中我们可以随时更改这个属性的指向,从而达到改变这个方法的目地。

class SomeClass
{
public Func<int> NextPrime
{
get;
private set;
}
int prime;
public SomeClass()
{
NextPrime = () =>
{
prime = ;
NextPrime = () => {
// 这里可以加上 第二次和第二次以后执行NextPrive()的逻辑代码
return prime;
};
return prime;
};
}
}

这里执行可以用

SomeClass a = new SomeClass();

Console.WriteLine(a.NextPrime());

上面的代码中当NextPrime第一次被调用的时候是2,与此同时,我们更改了NextPrime,我们可以把它指向另外的方法,和JavaScrtip的灵活性比起来也不差吧?如果你还不满意 ,那下面的代码应该能满足你。

Action<int> loopBody = i => {
if(i == )
loopBody = //把loopBody指向别的方法
/* 前10000次执行下面的代码 */
};
for(int j = ; j < ; j++)
loopBody(j);

在调用的地方我们不用考虑太多,然后这个方法本身就具有调优性了。我们原来的做法可能是在判断i==1000之后直接写上相应的代码,那么和现在的把该方法指向另外一个方法有什么区别呢?

三、自执行方法

JavaScript 中的自执行方法有以下几个优势:

1  不会污染全局环境

2  保证自执行里面的方法只会被执行一次

3  解释完立即执行

在C#中我们也可以有自执行的方法:

(() => {

// Do Something here!

})();

上面的是没有参数的,如果你想要加入参数,也非常的简单:

((string s, int no) => {
// Do Something here!
})("Example", );
.NET4.5最闪的新功能是什么?async?这里也可以
await (async (string s, int no) => {
// 用Task异步执行这里的代码
})("Example", );
// 异步Task执行完之后的代码

四、对象即时初始化

大家知道.NET为我们提供了匿名对象,这使用我们可以像在JavaScript里面一样随意的创建我们想要对象。但是别忘了,JavaScript里面可以不仅可以放入数据,还可以放入方法,.NET可以么?要相信,Microsoft不会让我们失望的。

//Create anonymous object
var person = new {
Name = "Jesse",
Age = ,
Ask = (string question) => {
Console.WriteLine("The answer to `" + question + "` is certainly 42!");
}
};
//Execute function
person.Ask("Why are you doing this?");

但是如果你真的是运行这段代码,是会抛出异常的。问题就在这里,Lambda表达式是不允许赋值给匿名对象的。但是委托可以,所以在这里我们只需要告诉编译器,我是一个什么类型的委托即可。

var person = new {
Name = "Florian",
Age = ,
Ask = (Action<string>)((string question) => {
Console.WriteLine("The answer to `" + question + "` is certainly 42!");
})
};

这时候再调用person.Ask("123");

但是这里还有一个问题,如果我想在Ask方法里面去访问person的某一个属性,可以么?

var person = new
{
Name = "Jesse",
Age = ,
Ask = ((Action<string>)((string question) => {
Console.WriteLine("The answer to '" + question + "' is certainly 20. My age is " + person.Age );
}))
};

结果是连编译都通不过,因为person在我们的Lambda表达式这里还是没有定义的,当然不允许使用了,但是在JavaScript里面是没有问题的,怎么办呢?.NET能行么?当然行,既然它要提前定义,我们就提前定义好了。

dynamic person = null;
person = new {
Name = "Jesse",
Age = ,
Ask = (Action<string>)((string question) => {
Console.WriteLine("The answer to `" + question + "` is certainly 42! My age is " + person.Age + ".");
})
};
//Execute function
person.Ask("Why are you doing this?");

C#进阶之路(四):拉姆达的更多相关文章

  1. 【SSH进阶之路】Hibernate映射——多对一单向关联映射(四)

    [SSH进阶之路]Hibernate基本原理(一) ,小编介绍了Hibernate的基本原理以及它的核心,採用对象化的思维操作关系型数据库. [SSH进阶之路]Hibernate搭建开发环境+简单实例 ...

  2. C#进阶之路(六):表达式进行类的赋值

    好久没更新这个系列了,最近看.NET CORE源码的时候,发现他的依赖注入模块的很多地方用了表达式拼接实现的.比如如下代码 private Expression<Func<ServiceP ...

  3. SqlSugar常用查询实例-拉姆达表达式

    SqlSugar支持拉姆达表达式查询,匿名对象参数等,相对还是比较方便好用的. 一.查询列表: //查询列表 SqlSugarClient db = SugarContext.GetInstance( ...

  4. MVC进阶之路:依赖注入(Di)和Ninject

    MVC进阶之路:依赖注入(Di)和Ninject 0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类, ...

  5. 【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一)

    文件夹      [SSH进阶之路]一步步重构容器实现Spring框架--从一个简单的容器開始(八)      [SSH进阶之路]一步步重构容器实现Spring框架--解决容器对组件的"侵入 ...

  6. Android研发进阶之路

    前言 移动研发火热不停,越来越多人开始学习android开发.但很多人感觉入门容易成长很难,对未来比较迷茫,不知道自己技能该怎么提升,到达下一阶段需要补充哪些内容.市面上也多是谈论知识图谱,缺少体系和 ...

  7. GO语言的进阶之路-初探GO语言

    GO语言的进阶之路-初探GO语言 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.为什么我们需要一门新语言 Go语言官方自称,之所以开发Go 语言,是因为“近10年来开发程序之难 ...

  8. Scala进阶之路-Scala中的高级类型

    Scala进阶之路-Scala中的高级类型 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类型(Type)与类(Class)的区别 在Java里,一直到jdk1.5之前,我们说 ...

  9. Scala进阶之路-Spark底层通信小案例

    Scala进阶之路-Spark底层通信小案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Spark Master和worker通信过程简介 1>.Worker会向ma ...

随机推荐

  1. 光标定位 + commonAncestor

    self.cmd.range.setStartBefore().collapse(true) self.cmd.select()   通过dom节点设置range的范围 <h1>conte ...

  2. str字符串、bool类型常用方法总结

    字符串拼接 必须是字符串与字符串拼接 print('马化腾'+'马云') print('马化腾' * 10) 将打印10个马化腾 字符串翻转 [ : :-1] 字符串可以加和乘,不能减和乘 input ...

  3. poj 2828 Buy Tickets 【买票插队找位置 输出最后的位置序列+线段树】

    题目地址:http://poj.org/problem?id=2828 Sample Input 4 0 77 1 51 1 33 2 69 4 0 20523 1 19243 1 3890 0 31 ...

  4. ubuntu 查看系统版本

    在终端中执行下列指令:cat /etc/issue可以查看当前正在运行的 Ubuntu 的版本号: 使用 lsb_release 命令也可以查看 Ubuntu 的版本号,与方法一相比,内容更为详细:

  5. H3C光模块相关命令和检测方法

    <Sysname>  dis transceiver interface GigabitEthernet 1/0/28  查看 GigabitEthernet1/0/28 transcei ...

  6. XXL-Job高可用集群搭建

    如果XXL-Job admin挂掉就完蛋了,所有任务无法执行 调度中心:管理任务的触发 调度中心如何实现集群? XXL-Job如何实现集群? 底层已经实现好了!文档里面有的 如果想实现Job集群:   ...

  7. 【P2052】道路修建(树形+搜索)

    这个题看上去高大上,实际上就是一个大水题.怎么说呢,这个题思路可能比较难搞,代码实现难度几乎为0. 首先我们可以发现这是一棵树,然后问其中任意一条边左右两边的点的数量之差的绝对值,实际上,无论两边的点 ...

  8. MyBatis之xml配置配置文件(一)(xml配置文件)

    一.properties标签 <properties resource="jdbc.properties"></properties> 使用properti ...

  9. Codeforces Round #280 (Div. 2) D. Vanya and Computer Game 数学

    D. Vanya and Computer Game time limit per test 2 seconds memory limit per test 256 megabytes input s ...

  10. Java 创建线程的两种方法

    Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对象描述了一个单独的线程.要产生一个线 ...