转载:
1. 前言
2. Lambda表达式概念
3. Lambda表达式树的概念和示例
4. Lambda表达式的简单应用
5. 总结
前言
在LINQ刚发布的时候,一直也没有时间去研究下LINQ,特别是在当时各种LINQ to ***纷纷出现,看得眼花缭乱。
在LINQ中增加的lambda表达式一直觉得挺神秘的,看到各位高手把lambda表达式运用得如火纯清,很是羡慕. 最近抽
空学习了解了下LINQ, 对学习的过程进行记录。文章基础,高手请飘过 :-)
计划学习的主要内容是lambda表达式,LINQ to Objects, LINQ to XML. 三个部分。
学习之前,推荐一款帮助学习LINQ的优秀工具LINQPad,下载地址:LINQPad下载 。
【另注:学习过程未免出现差错,欢迎指正】
Lambda表达式的概念
什么是lambda表达式?Lambda 表达式是一种匿名函数,它可以包含表达式和语句,并且可用于创建委托或
表达式目录树类型。我们使用lambda表达式可以帮助我们编写精简和紧凑的代码,许多操作中允许自定义排序和过
滤的函数,在.NET2.0的时候通常使用委托函数来实现,在.NET3.5可以使用lambda表达式。
现在举例说明lambda表达式: Func<int,int> addOne= item=> item+1 ,其中操作符 “=>”读作“Goes to”,
可以理解为操作符左边的是函数的参数,操作符右边是函数体内容。上面我们定义的lambda表达式等同于函数如下:
1
int
addOne(
int
item)
2
{
3
return
item
+
1
;
4
}
那么什么样的表达式才是合法的lambda表达式呢?
1. lambda表达式可以是多个参数。 如: (item1,item2)=>item1+item2;
2. lambda表达式可以是0个参数。 如: ()=>"csharp";
3. lambda表达式可以显示指定参数类型。 (int item1,string item2)=>item1+item2;
4. lambda表达式函数体可以使用多条语句. (item1)=>{string ret="hello"+item1;return ret;};
使用lambda表达式的时候,不得不提到泛型委托。在上面我们定义的表达式如:(item1,item2)=>item1+item2;
只是定义的表达式,我们如何调用呢?我们可以定义自己的函数委托来引用lambda表达式,如下
1
public
delegate
int
addOneDelegate(
int
item1,
int
item2);
2
void
Main()
3
{
4
addOneDelegate fun
=
(item1,item2)
=>
item1
+
item2;
5
var result
=
fun(
123
,
456
);
6
result.Dump(
"
结果
"
);
7
}
8
9
10
.结果
11
579
在这里我们可以使用.NET类库中已经提供的泛型委托Func<T>和Action<T>来引用lambda表达式.代码如下
1
void
Main()
2
{
3
Func
<
int
,
int
>
fun
=
(item1,item2)
=>
item1
+
item2;
4
var result
=
fun(
123
,
456
);
5
result.Dump(
"
结果
"
);
6
}
7
8
.结果
9
579
关于Func<T>是泛型委托,最后的一个类型是指返回结果的类型,前面都是输入参数类型,上面的例子中,我们
的输入类型是INT,返回类型也是INT。同样如果我们定义Func<int,string,bool>,是指输入参数有两个,一个是int
类型,一个是string类型,函数返回是bool类型。使用泛型委托可以帮助我们方便引用lambda表达式。Func<T>提供
了多个重载,如下
1
public
delegate
T Func
<
T
>
();
2
public
delegate
T Func
<
A0, T
>
(A0 arg0);
3
public
delegate
T Func
<
A0, A1, T
>
(A0 arg0, A1 arg1);
4
public
delegate
T Func
<
A0, A1, A2, T
>
(A0 arg0, A1 arg1, A2 arg2);
5
public
delegate
T Func
<
A0, A1, A2, A3, T
>
(A0 arg0, A1 arg1, A2 arg2, A3 arg3);
在这里,需要提到一些关于lambda表达式的特性和规则。
1. lambda表达式的引用变量必须是显式类型。编译器对lambda表达式的类型推断是通过返回的引用变量的类型指定。
如下面的语句是非法的。
1
void
Main()
2
{
3
var c
=
n
=>
n
+
1
;
//
Error,Cannot assign lambda expression to an implicitly-typed local variable
4
Func
<
string
>
cc
=>
n
+
1
;
//
Ok
5
}
2. 在lambda表达式中可以直接访问本地变量和全局变量。
1
public
static
string
grobalVar
=
"
grobal string
"
;
2
void
Main()
3
{
4
string
localVar
=
"
local string
"
;
5
Func
<
string
,
string
>
fun
=
n
=>
n
+
"
can access
"
+
grobalVar
+
"
and
"
+
localVar;
6
fun(
"
lambda
"
).Dump();
7
}
8
9
10
结果:
11
lambda can access grobal
string
and local
string
12
3. lambda表达式的参数可以是ref或out方式传入,在通过ref或out方式传入的时候必须指定参数的具体类型。
1
public
delegate
int
RefParameterFunction(
ref
int
n);
2
void
Main()
3
{
4
int
x
=
10
;
5
RefParameterFunction fun
=
(
ref
int
n)
=>
n
++
;
6
fun(
ref
x);
7
x.Dump();
8
}
9
10
11
结果
12
11
4. lambda表达式的参数可以支持不定参数数传入。
1
public
delegate
int
AddFunction(
params
int
[] ints);
2
void
Main()
3
{
4
int
[] x
=
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
};
5
AddFunction fun
=
(items)
=>
6
{
7
int
count
=
0
;
8
foreach
(
int
item
in
items)
9
{count
=
count
+
item;}
10
return
count;
11
};
12
fun(x).Dump(
"
求和:
"
);
13
}
14
15
16
求和:
17
45
Lambda表达式树的概念和示例
Lambda另一个强大的特性就是表达式树,lambda表达式都可以通过表达式树来描述,就不用在代码
中直接编写表达式。这样的优势就是表达式可以在运行的时候编译运行,而且可以对lambda表达式进行动态修改。
要使用lambda表达式树,首先提到一个表达式的泛型类Expression<T>,(域名空间System.Linq.Expressions),
这个类是保存表达式的结构信息。我们把Expession看作一棵树结构,每个结点都是由两部分组成,左树和右树,一直这样
递归下去。这里需要说明一下,刚开始使用表达式树的时候容易和表达式产生混淆,比如:
1
void
Main()
2
{
3
4
Expression
<
Func
<
int
,
int
>>
tree
=
x
=>
x
+
1
;
5
Func
<
int
,
int
>
exp
=
x
=>
x
+
1
;
6
7
tree(
1
);
//
'tree' is a 'variable' but is used like a 'method'
8
exp(
1
);
//
输出2
9
}
10
11
注意:tree只是lambada表达式的树形结构信息,并不是函数可以直接调用。
现在我们对lambda表达式的树结构输出来查看下,举例:我们编写一个验证三角形是否直角三角形,通过沟谷定律,
我们很容易编写lambda表达式为:
1
(x,y,z)
=>
(x
*
x
+
y
*
y)
==
z
*
z
现在我们使用LINQPad的Dump()函数进行输出显示:
1
void
Main()
2
{
3
Expression
<
Func
<
int
,
int
,
int
,
bool
>>
tree
=
(x,y,z)
=>
(x
*
x
+
y
*
y)
==
z
*
z;
4
tree.Dump();
5
}
输出结果如下:
通过输出的图形,我们可以清楚的看出整个lambda表达式是由LEFT和RIGHT两部分组成的,Left部分和right部分之间
的关系通过 NodeType属性指定,所有的NodeType类型通过枚举(System.Linq.Expressions.ExpressionType )定义,
而结点的Type可以看作返回类型,比如我们定义的 tree的Type是Func<int,int,int,bool>,而Type是Lambda。
那么如何把表达式树转换为可以直接使用的函数呢?Expression类提供了函数Compile(),就可以把我们定义的lambda
表达式树编译为实际的函数,代码如下:
1
void
Main()
2
{
3
Expression
<
Func
<
int
,
int
,
int
,
bool
>>
tree
=
(x,y,z)
=>
(x
*
x
+
y
*
y)
==
z
*
z;
4
Func
<
int
,
int
,
int
,
bool
>
fun
=
tree.Compile();
5
fun(
3
,
4
,
5
).Dump();
6
}
7
9
结果
10
True
我们了解到了lambda表达式树的基本概念,现在我们自行构造一个lambda表达式树。还是以上面的验证是否是直角
三角形为例,我们通过System.Linq.Expressions提供了表达式类来构造这个表达式,不参考LINQPad输出的结构。现在
我们分析表达式的树结构,(x,y,z) => (x*x + y*y)== z*z 按照操作符把表达式分为left tree和right tree。比如首先
我们把整个表达式分为左树:x*x + y*y, 右树:z*z, 关系:Equal,以此画出阿里如下:
我们已经把表达式树分析出来,现在我们开始使用.NET提供的表达式类来构造这棵表达式树,在这棵树比较简单,
我们比较用到的类包括二元表达式类(BinaryExpression)和参数表达式类(ParameterExpression)。现在我们
从树的叶结点开始构造。
首先我们需要制定表达式中参数和参数的类型。
1
ParameterExpression expX
=
Expression.Parameter(
typeof
(
int
),
"
x
"
);
2
ParameterExpression expY
=
Expression.Parameter(
typeof
(
int
),
"
y
"
);
3
ParameterExpression expZ
=
Expression.Parameter(
typeof
(
int
),
"
z
"
);
接着我们使用二元表达式将参数表达式关联起来,X和X,Y和Y,Z和Z,二元关系都是乘.
1
BinaryExpression mulX
=
Expression.Multiply(expA, expA);
2
BinaryExpression mulY
=
Expression.Multiply(expY, expY);
3
BinaryExpression mulZ
=
Expression.Multiply(expZ, expZ);
然后我们将X*X+Y*Y通过 加二元表达式关联起来.
1
BinaryExpression addXY
=
Expression.Add(mulX,mulY);
最后我们将X*X+Y*Y 和Z*Z通过 等于二元表达式关联起来.
BinaryExpression final
=
Expression.Equal(
mulZ, addXY
);
现在我们构造完成后,可以通过编译来执行,下面是完整的代码:
1
void
Main()
2
{
3
ParameterExpression expX
=
Expression.Parameter(
typeof
(
int
),
"
x
"
);
4
ParameterExpression expY
=
Expression.Parameter(
typeof
(
int
),
"
y
"
);
5
ParameterExpression expZ
=
Expression.Parameter(
typeof
(
int
),
"
z
"
);
6
BinaryExpression mulX
=
Expression.Multiply(expX, expX);
7
BinaryExpression mulY
=
Expression.Multiply(expY, expY);
8
BinaryExpression mulZ
=
Expression.Multiply(expZ, expZ);
9
BinaryExpression addXY
=
Expression.Add(mulX,mulY);
10
BinaryExpression final
=
Expression.Equal(mulZ, addXY);
11
Expression
<
Func
<
int
,
int
,
int
,
bool
>>
square
=
Expression.Lambda
<
Func
<
int
,
int
,
int
,
bool
>>
(final, expX, expY, expZ);
12
Func
<
int
,
int
,
int
,
bool
>
xx
=
square.Compile();
13
xx(
3
,
4
,
5
).Dump();
14
}
15
16
17
结果:
18
True
19
Lambda表达式的简单应用
1. 对数组的自定义排序。
1
void
Main()
2
{
3
string
[] items
=
{
"
csharp
"
,
"
cpp
"
,
"
python
"
,
"
perl
"
,
"
java
"
};
4
List
<
string
>
list
=
items.ToList();
5
list.Sort((x,y)
=>
y.Length
-
x.Length);
6
7
list.Dump();
8
}
9
10
结果:
11
python
12
csharp
13
perl
14
java
15
cpp
2. 对数组数据进行搜索
1
void
Main()
2
{
3
string
[] items
=
{
"
csharp
"
,
"
cpp
"
,
"
python
"
,
"
perl
"
,
"
java
"
};
4
List
<
string
>
list
=
items.ToList();
5
var result
=
list.FindAll(x
=>
x.Length
==
4
);
6
7
result.Dump();
8
}
9
10
结果:
11
perl
12
java
3. 对数组数据进行直接更新
1
void
Main()
2
{
3
string
[] items
=
{
"
csharp
"
,
"
cpp
"
,
"
python
"
,
"
perl
"
,
"
java
"
};
4
var result
=
items.Select(n
=>
n
+
"
:
"
+
n.Length);
5
result.Dump();
6
}
7
8
结果:
9
csharp :
6
10
cpp :
3
11
python :
6
12
perl :
4
13
java :
4
总结
这篇学习记录对Lambda表达式和Lambda表达式树的最基础进行描述,对lambda没有更多深入的研究。比如在表达式树
的的动态修改,更多复杂的lambda表达式,lamdba表达式树对更复杂函数的构造。如果有兴趣,可以继续研究。
另:这是我发布的第一篇随笔,写一篇随笔原来也是挺艰难,水平有限,希望大家指正,谢谢。
---------------------------------------------------------------------
作者: jordan51341 ([email protected])
欢迎转载,转载请注明原文地址:http://www.cnblogs.com/jordan51341
---------------------------------------------------------------------