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可以看到,编译器帮我们自动创建了一个类,用于保存本地变量,以扩展它们的生命周期