Linq学习总结(4)——Lambda表达式

Lambda表达式的演化

要了解Lambda表达式,我们首先应从委托说起。.NET中的委托实际上就是C语言中的函数指针,函数通过地址进行引用,只不过.NET中它更加好看了而已。

delegate void FunctionPoint(string str);

static void printHello(string name)
{
Console.WriteLine(
"Hello {0}", name);
}

static void Main(string[] args)
{
FunctionPoint fp
= printHello;
fp(
"heqichang");
}
/*Ouput
* Hello heqichang
*/

然后,委托在.NET2.0中又被精简成了匿名委托

delegate void FunctionPoint(string str);

static void Main(string[] args)
{
FunctionPoint fp
= delegate(string name)
{
Console.WriteLine(
"Hello {0}",name);
};
fp(
"heqichang");
}
/*Ouput
* Hello heqichang
*/

匿名委托省略了函数名、返回类型以及参数类型,变得更加轻量

delegate void FunctionPoint(string str);

static void Main(string[] args)
{
FunctionPoint fp
= s => Console.WriteLine("Hello {0}",s);
fp(
"heqichang");
}
/*Ouput
* Hello heqichang
*/

现在我们看到的表达式就是通过委托一步一步简化而来的。我们的Lambda表达式就是一个简洁的委托。左侧(相对于=>)代表函数的参数,右侧就是函数体。

在System命名空间中,微软已经为我们预定义了几个泛型委托Action、Func、Predicate。Action用于在泛型参数上执行一个操作;Func用于在参数上执行一个操作并返回一个值;Predicate<T>用于定义一组条件并确定参数是否符合这些条件。

 

表达式树

Lambda表达式还有个重要的用途就是用来构建表达式树:

static void Main(string[] args)
{
Expression
<Func<int, int, int>> exp = (a, b) => a * (b + 2);

ParameterExpression param1
= (ParameterExpression)exp.Parameters[0];
ParameterExpression param2
= (ParameterExpression)exp.Parameters[1];
BinaryExpression operation
= (BinaryExpression)exp.Body;
ParameterExpression left
= (ParameterExpression)operation.Left;
BinaryExpression operation2
= (BinaryExpression)operation.Right;
ParameterExpression left2
= (ParameterExpression)operation2.Left;
ConstantExpression right2
= (ConstantExpression)operation2.Right;

Console.WriteLine(
"Decomposed expression: ({0},{1}) => {2} {3} ({4} {5} {6})",
param1.Name, param2.Name, left.Name, operation.NodeType, left2.Name,
operation2.NodeType, right2.Value);

Func
<int, int, int> func = exp.Compile();
Console.WriteLine(func(
2,2));
/*Ouput
* 8
*/
}

我们上面的lambda表达式构建了这么一个表达式树:

闭包

如果将一个变量声明在一个函数内部,该变量就只会在该函数的栈内存中。当函数返回,这个本地变量也同时从栈内存中被清除了。当你在Lambda表达式中使用本地变量时,该变量就会在函数的栈空间清理时被移除。为了防止这样的事发生,当一个依赖于本地变量的Lambda表达式需要从函数中返回时,编译器就会创建一个闭包(Closure,也就是一个包装器类)。

static void Main(string[] args)
{
int x = 1;
Func
<int, int> add = y => x + y;
Console.WriteLine(add(
3));
/*Output
* 4
*/
}

我们通过ILDasm可以看到,编译器帮我们自动创建了一个类,用于保存本地变量,以扩展它们的生命周期

posted @ 2011-06-28 14:15  heqichang  阅读(465)  评论(0编辑  收藏  举报