>>lambda表达式就是用来代替委托实例的未命名方法。
编译器会把lambda表达式转化为以下二者之一:
1. 一个委托实例;
2. 一个表达式树(expression tree),类型是Expression<TDelegate>,它表示了可遍历的对象模型中lambda表达式的代码;它允许lambda表达式延迟到运行时再被解释。
delegate int Transformer(int i);
Transformer sqr = x=>x*x;
Console.WriteLine(sqr(3));
//编译器会通过编写一个私有方法来解析这个lambda表达式,然后把表达式的代码移动到这个方法里
>>lambda表达式的形式
(parameter)=> expression or statement-block
如果只有一个参数并且参数类型是可推断的话,那么参数的小括号可以省略。
>>lambda表达式与Action和Func
lambda表达式通常与Action和Func一起使用
Func<int, int> sqr = x=>x*x;
Func<int, int> getLen = (str1, str2)=>str1.length()+str1.length();
int totalLen = getLen("hello", "world");
>>显示声明参数类型
void Foo<T>(T t){//...}
void Bar<T>(Action<T> a){//...}
void main()
{
Bar(x => Foo(x)); //无法通过编译
Bar((int x) => Foo(x)); //申明类型
Bar<int>(x => Foo(x));
Bar<int>(Foo); //methods group
}
>>捕获外部变量
lambda表达式可以引用本地的变量和所在方法的参数。
被引用的外部变量叫做被捕获的变量(captured variable);
捕获了外部变量的lambda表达式称为闭包;
被捕获的变量实在实际被调用的时候才会被计算,而不是被捕获的时候;
int factor = 2;
Func<int, int> multiplier = x=>x*factor;
factor = 10;
Console.WriteLine(multiplier(3)); //30
lambda表达式本身也可以更新被捕获的对象;
int seed=0;
Func<int> natural = () => seed++;
Console.WriteLine(natural()); //0
Console.WriteLine(natural()); //1
Console.WriteLine(seed); //2
被捕获变量的生命周期会被延长到和委托一样;
static Func<int> Natural()
{
int seed =0;
return ()=>seed++;
}
void main()
{
Func<int> natural = Natural();
Console.WriteLine(natural()); //0
Console.WriteLine(natural()); //1
}
lambda表达式里面,实例化的变量对于委托实例的每次调用来说都是唯一的;
static Func<int> Natural()
{
return ()=>{int seed =0; return seed++;}
}
void main()
{
Func<int> natural = Natural();
Console.WriteLine(natural()); //0
Console.WriteLine(natural()); //0
}
捕获迭代变量
捕获迭代变量时,C#会把这个变量当作循环的外部定义的变量,这意味着每次捕获的都是同一个值;
Action[] actions = new Action[];
for(int i=0;i<3;i++)
{
actions[i] = () => WriteLine(i);
}
foreach(a in actions)
{
a(); //333
}
//相当于如下的写法
Action[] actions = new Action[];
int i = 0;
actions[1] = () => WriteLine(i);
i = 1;
actions[1] = () => WriteLine(i);
i = 2;
actions[1] = () => WriteLine(i);
i = 3;
foreach(a in actions)
{
a(); //333
}
//解决方法
Action[] actions = new Action[];
for(int i=0;i<3;i++)
{
int loopInt = i; //因为此处的变量在每个loop中保存在不同的内存地址
actions[i] = () => WriteLine(loopInt);
}
foreach(a in actions)
{
a(); //012
}
lambda表达式与本地方法
匿名方法 vs lambda表达式
匿名方法和lambda表达式很像,但是:
1. 不能使用隐式类型参数;
2. 不能使用表达式语法,必须使用语法块;
3. 没有编译表达树的能力,不能赋值给Expression<I>;