C3新技术之Lambda表达式学习总结Word文档格式.docx
《C3新技术之Lambda表达式学习总结Word文档格式.docx》由会员分享,可在线阅读,更多相关《C3新技术之Lambda表达式学习总结Word文档格式.docx(27页珍藏版)》请在冰豆网上搜索。
//error
所有对于匿名方法的约束也同样适用于Lambda式。
请参阅AnonymousMethods(C#ProgrammingGuide).
2.从表达式理解C#LambdaExpressions
由一个计算表达式组成的一个Lambda式称之为表达式Lambda。
表达式Lambda常被用于构造表达式树。
一个表达式Lambda返回计算表达式运算的结果。
基本结构如下:
1.(inputparameters)=>
expression
3.//如果只有一个输入参数时,括号可以省略。
5.//如果具有一个以上的输入参数,必需加上括号。
6.
7.(x)=>
x*x等于x=>
x*x
8.
9.(x,y)=>
x==y
10.
11.//可以显式指定输入参数的类型
12.
13.(intx,strings)=>
s.Length>
x
14.
15.//也可以没有任何输入参数
16.
17.()=>
SomeMethod1()
上面这段代码在Lambda式中调用了一个方法。
需要注意的是,如果在创建会被其他方使用的表达式树的时候,不宜在Lambda式中执行方法调用。
比如:
在SQLServer内执行。
一般来说,让一个方法在原先设计的上下文环境以外执行没有意义,也不能真正工作。
3.从语句理解C#LambdaExpressions
语句Lambda和表达式Lambda非常相似,只是语句被包含在大括号内:
{statement;
}
大括号中的语句可以是任意多条,也可以写成多行(定义一个Lambda式也就是在定义一个匿名方法):
1.TestDelegatemyDel=n=>
{
2.strings=n+"
"
+"
World"
;
3.Console.WriteLine(s);
};
当然语句Lambda跟匿名方法一样,无法用于创建表达式树。
4.C#LambdaExpressions之类型猜测
当编写一个Lambda式的时候,我们通常不需要明确指定输入参数的类型。
因为编译器会根据Lambda体的实现,以及委托的定义来猜测类型。
举例:
如果要从一个List<
int>
中删除小于100的元素
1.lst.RemoveAll(i=>
i<
100);
//i会被猜测为int
通常的猜测规则如下:
◆Lambda式必须包含与委托定义中相等数量的输入参数;
◆每个Lambda式的输入参数必须能够隐式转换成委托定义中所要求的输入参数;
◆Lambda式的返回值必须能够隐式转换成委托定义中的返回值。
注意:
由于目前在commontypesystem中还没有一个“Lambda式类型”的类型。
如果在有些场合提到“Lambda式的类型”,那通常表示委托的定义或者是Expression<
>
类型。
5.C#LambdaExpressions变量作用域
在Lambda式定义中可以引用外部变量。
只要是在定义处能够访问到的变量,都可以在Lambda式中引用。
Lambda式的定义仅仅是定义一个匿名方法,最终会生成一个委托对象。
外部变量的引用将被“捕获”到委托对象内部,将会伴随委托对象的整个生命周期。
在委托对象生命周期结束之前该变量都不会被垃圾回收。
就算外部变量已经超过了原来的作用域,也还能继续在Lambda式中使用。
所有会被引用的外部变量必须在Lambda式定义之前被显式赋值。
见下例
1.delegateboolD();
2.delegateboolD2(inti);
3.classTest
4.{
5.Ddel;
6.D2del2;
7.publicvoidTestMethod(intinput)
8.{
9.intj=0;
10.//Initializethedelegateswithlambdaexpressions.
11.//Noteaccessto2outervariables.
12.//delwillbeinvokedwithinthismethod.
13.del=()=>
{j=10;
returnj>
input;
15.//del2willbeinvokedafterTestMethodgoesoutofscope.
16.del2=(x)=>
{returnx==j;
17.//Demonstratevalueofj:
18.//Output:
j=0
19.//Thedelegatehasnotbeeninvokedyet.
20.Console.WriteLine("
j={0}"
j);
21.//Invokethedelegate.
22.boolboolResult=del();
23.//Output:
j=10b=True//注意j在del的执行过程中被修改
24.Console.WriteLine("
j={0}.b={1}"
j,boolResult);
25.}
26.staticvoidMain()
27.{
28.Testtest=newTest();
29.test.TestMethod(5);
30.//Provethatdel2stillhasacopyof
31.//localvariablejfromTestMethod.
32.
33.//j的引用超出了原先定义的作用域
34.boolresult=test.del2(10);
35.//Output:
True
36.Console.WriteLine(result);
37.Console.ReadKey();
38.}
39.}
40.
下面是关于变量作用域的规则:
◆被“捕获”的变量在委托的生命周期结束前都不会被垃圾回收;
Lambda所使用的变量,在委托的生命周期内不会被垃圾回收.要让委托承载的方法数超出范围估计很难达到,这个具体委托能承载多少个方法,我也想知道,我试了加载100W个方法,也没出现问题,这个应该和内存有关,只要你机器有足够的内存.
◆在Lambda式内部定义的变量对外不可见;
delegateboolD();
Ddel;
for(inta=0;
a<
4;
a++)
{
del+=()=>
{intt=0;
returntrue;
Console.WriteLine("
t={0}"
t);
//这里无法访问Lambda中定义的变量t
}
◆Lambda式无法直接捕获一个具有ref或out描述的参数变量;
如果委托中有ref或out参数,则Lambda必须有参数类型(即显示类型)
delegateboolD2(inti,refstringstr);
D2del2;
for(inta=0;
del2+=(intx,refstringc)=>
{c=c+"
j"
Console.WriteLine(x+c);
//这里必须显示参数类型x,c}
strings="
sadf"
del2(5,refs);
Console.WriteLine(s);
◆Lambda式中的return语句不会导致当前所在的方法返回;
Lambda表达式中return只是跳出Lambda,然后继续执行方法中Lambda下面的语句,不会跳出当前所在的方法.
◆Lambda式中不允许包含会导致跳出当前执行范围的goto,break或continue语句。
del+=()=>
break;
//这里会出现如下语法错误Controlcannotleavethebodyofananonymousmethodorlambdaexpression
6.C#LambdaExpressions学习的总结
Lambda式可以说就是另外一种形式的匿名方法。
用在某些地方,会使代码更加简洁。
定义一个Lambda式本质上就是定义一个委托的实现体。
Lambda表达式学习
Lambda表达式可以简化C#编程的某些方面,用法非常灵活。
因此也不容易掌握。
1、Lambda表达式是与委托紧密联系的。
Lambda表达式的运算符是=>
。
运算符左边列举出了需要的参数,右边定义了赋予Lambda变量的方法的实现代码。
下面这段代码是一个最简单的使用方法:
[csharp]
viewplaincopy
1.public
class
MyLambda
2.{
3.
public
void
disPlay()
4.
{
5.
string
mid
=
"
middle
part,"
6.
Func<
string,
string>
lambda
param
=>
7.
8.
+=
mid;
9.
and
this
was
added
to
the
string"
10.
return
param;
11.
};
12.
Console.WriteLine(lambda("
Start
of
));
13.
}
14.}
string,string>
是一个委托类型。
包含两个参数:
一个输入的string和一个输出的string.
param是输入参数,所以他的类型可以认为是string类型的(当然很多地方会出现没有名字的类型的)。
右边表示一个方法,这个方法没有名字。
这个方法赋予了变量lambda。
在本例中,通过Console.WriteLine(lambda("
Startofstring"
向lambda方法传递参数“Startofstring”.经处理后会有这样的输出:
Startofstring,middlepart,andthiswasaddedtothestring。
所以我想这样理解:
2、Lambda表达式的参数。
lambda表达式如果只有一个参数,只写出参数名就行了。
下边的lambda表达式使用了参数s,因为委托类型定义了一个string类型。
所以s的类型就是string。
实现函数体的代码调用了String.Format()方法返回一个字符串,在调用委托是就直接把字符串输出了:
1.Func<
oneParam
s
2.
String.Format("
Change
To
UpperCase
{0}"
s.ToUpper());
7.Console.WriteLine(
oneParam("
abc"
注:
是一个系统定义的带有一个输入和一个输出的委托。
如果委托有多个参数,就要把参数放到括号中,如下:
double,
double>
twoParam
(x,
y)
x
*
y;
3、单行代码和多行代码
如果Lambda表达式只有一条语句,在方法块内就不需要花括号和return语句了,编译器会自动添加一条隐式的return语句。
如上边的两个表达式可以分别表示为:
2.Func<
但是如果Lambda表达式实现代码需要多条语句时,就必须添加花括号和return语句了。
如第一个例子的代码。
4、Lambda表达式的外部变量
Lambda表达式可以使用外部变量。
但使用时应该注意些问题:
先看下边的代码:
1.int
someVar
5;
int,
f
+
someVar;
3.someVar
10;
4.Console.WriteLine(f(5));
输出应该是x+5还是x+10呢?
运行下发现输出15(x+10),即修改外部变量someVar后,会使用外部变量的新值。
对于表达式x=>
x+someVar;
编译器会创建一个匿名类,他有一个构造函数来传递外部变量。
该构造函数取决于从外部传递过来的变量个数。
对于上边这个例子,我们可以认为构造函数接受一个int,匿名类包含了一个匿名方法,其实现代码和返回类型有lambda表达式定义:
AnonymousClass
private
int
AnonymousClass(int
someVar)
this.someVar
AnonymousMethod(int
x)
12.}
使用Lambda表达式时,调用该方法(AnonymousMethod(intx)),会创建匿名类的一个实例(相当于newAnonymousClass(somevar)),并传递调用该方法时的变量的值。
5、泛型Action<
T>
委托和Func<
委托是系统定义的两个泛型委托。
Action<
委托表示引用一个返回类型为Void的方法。
这个委托存在不同的变体,可以传递最多16个不同的参数类型。
同时,没有泛型参数的Action类可以调用没有参数的方法。
例如,Action<
inT>
表示有一个输入参数的方法,Action<
inT1,inT2>
表示有两个输入参数的方法。
Func<
可以以类似的方法使用。
不过Func<
允许调用带返回参数的方法。
也有不同的变体,最多可以传递16个参数和一个返回类型。
例如:
outTResult>
委托类型可以无参的带返回类型的方法,Func<
inT1,inT2,outTresult>
表示带两个参数和一个返回类型的方法。
需要记住一个东西,Action<
中的T可以有多个,但这些T类型都表示不同的输入类型。
可以表示带输出的方法,T可以有多个,且只有最后一个表示输出即最后一个是返回类型。
中的字符in、out在实际代码中是不会出现的。
在VS中,可以通过IntelliSense查看:
Action<
的16参数封装:
16参加一个返回的封装
下边通过一个简单的代码演示一下这两个泛型委托与一般委托的异同。
第一步:
先定义俩函数:
double
MultiplyByTwo(double
2;
4.}
5.public
Square(double
6.{
x;
8.}
这俩函数有共同的特征:
输入和返回类型都是double
第二步:
定义委托数组,并用这两个方法的方法名初始化数组:
1.delegate
DoubleOp(double
x);
3.DoubleOp[]
MyDoubleOp
this.MultiplyByTwo,
this.Square
10.Func<
[]
myFunc
//
14.
最后:
查看输出
1.for
(int
i
0;
<
MyDoubleOp.Length;
i++)
Console.WriteLine(MyDoubleOp[i](1.414));
7.for
myFunc.Length;
8.{
Console.WriteLine(myFunc[i](2.236));
10.}
比较一下,其实泛型委托和自定义的委托在使用上没什么不同。
只不过泛型委托Func<
系统已经为我们定义好了,直接使用就可以了,不需要再进行这样的定义delegatedoubleDoubleOp(doublex);
另一个泛型委托Action<
的使用也是一样的,只不过不能有返回类型而已。
贴出完整的代码:
一个简单的类MyClass类:
MyClass
delegate
myDelegate()
15.
16.
DoubleOp[]
17.
18.
19.
20.
21.
22.
for
23.
24.
25.
26.
27.
myFunc