C#3.0新体验(五)Lambda表达式
[转] http://blog.csdn.net/maotin/archive/2008/10/20/3110451.aspx
C#3.0新体验(五)
maotin
20081011
前言:不论多高的山,都是一步一步爬上去的,因为一次只能脚踏实地地迈出一步;但是迈出步伐的频率我们可以控制,速度快、步伐大,就会比别人先到山顶;工作、生活也是如此,效率高速度快,离成功就会更近一步。
六.Lambda表达式
1.什么是Lambda表达式
“Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。 所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。该 Lambda 运算符的左边是输入参数(如果有),右边包含表达式或语句块。
Lambda 表达式: x => x * x
要了解Lambda我们需要先来看看匿名函数,什么是匿名函数?匿名函数是一个“内联”语句或表达式,可在需要委托类型的任何地方使用。共有两种匿名函数:Lambda表达式(C#3.0)和匿名方法(C#2.0);这个“内联”好像不是很好理解,因此我们从委托的使用历史开始详细讲解一下匿名函数的产生与使用:(委托是一种包装方法调用的类型。就像类型一样,可以在方法之间传递委托实例,并且可以像方法一样调用委托实例。)
C#1.0:在2.0以前声明委托的唯一方法是使用命名方法,也就是说通过使用在代码中其他位置定义的方法显式初始化委托来创建委托的实例;例如:
// Declare a delegate
delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// Original delegate syntax required
// initialization with a named method.
TestDelegate testdelA = new TestDelegate(TestClass.DoWork);
// Invoke the delegates.
testdelA("Hello.");
}
// The method associated with the named delegate:
static void DoWork(string k)
{
System.Console.WriteLine(k);
}
}
delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// Original delegate syntax required
// initialization with a named method.
TestDelegate testdelA = new TestDelegate(TestClass.DoWork);
// Invoke the delegates.
testdelA("Hello.");
}
// The method associated with the named delegate:
static void DoWork(string k)
{
System.Console.WriteLine(k);
}
}
C#2.0:到了2.0引入了匿名方法的概念,作为一种编写可在委托调用中执行的未命名内联语句块的方式,也就是说可以不需要事先创建单独的方法,因此减少了实例化委托所需的编码系统开销,例如:
// Declare a delegate
delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// C# 2.0: A delegate can be initialized with
// inline code, called an "anonymous method." This
// method takes a string as an input parameter.
TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };
// Invoke the delegates.
testDelB("Hello 2 ");
}
}
delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// C# 2.0: A delegate can be initialized with
// inline code, called an "anonymous method." This
// method takes a string as an input parameter.
TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };
// Invoke the delegates.
testDelB("Hello 2 ");
}
}
C#3.0:引入了 Lambda 表达式,这种表达式与匿名方法的概念类似,但更具表现力并且更简练。Lambda 表达式逐渐取代了匿名方法,作为编写内联代码的首选方式。例如:
// Declare a delegate
delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// C# 3.0. A delegate can be initialized with
// a lambda expression. The lambda also takes a string
// as an input parameter (x). The type of x is inferred by the compiler.
TestDelegate testDelC = (x) => { Console.WriteLine(x); };
// Invoke the delegates.
testDelC("Hello 3 .");
}
}
delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// C# 3.0. A delegate can be initialized with
// a lambda expression. The lambda also takes a string
// as an input parameter (x). The type of x is inferred by the compiler.
TestDelegate testDelC = (x) => { Console.WriteLine(x); };
// Invoke the delegates.
testDelC("Hello 3 .");
}
}
2.Lambda表达式格式:
(参数列表)=> 表达式或语句块,左边参数列表可以有多个参数,一个参数,或者无参数;参数类型可以隐式或者显式。例如:
(x, y) => x * y //多参数,隐式类型=> 表达式
x => x * 10 //单参数,隐式类型=>表达式
x => { return x * 10; } //单参数,隐式类型=>语句块
(int x) => x * 10 //单参数,显式类型=>表达式
(int x) => { return x * 10; } //单参数,显式类型=>语句块
() => Console.WriteLine() //无参数
由上面这些语句我们可以得出Lambda表达式的一些要点:
a.参数类型可以忽略,因为可以根据使用的上下文进行推断而得到;
// Declare a delegate
delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// 从委托定义的参数可以推断出x是String类型.
TestDelegate testDelC = (x) => { Console.WriteLine(x); };
}
}
b.表达式的主体可以是表达式((x, y) => x * y ),也可以是语句块(x => { return x * 10; })delegate void TestDelegate(string s);
class TestClass
{
static void Main()
{
// 从委托定义的参数可以推断出x是String类型.
TestDelegate testDelC = (x) => { Console.WriteLine(x); };
}
}
c.Lambda表达式传入的实参将参与类型推断,以及方法重载辨析。
d.Lambda表达式表达式和表达式体可以被转换为表达式树
3.Lambda表达式与委托类型
// Declare a delegate
public delegate int TestIntDelegate(int g);
class TestClass
{
static void Main()
{
// 传递Lambda表达式
Test(y => y * y);
}
public void Test(TestIntDelegate ti)
{
int area = ti(10);
}
}
方法Test参数是委托TestIntDelegate类型,而我们在调用时直接传入了Lambda表达式Test(y => y * y);因此Lambda表达式可以被转成委托类型,但必须满足以下几点:public delegate int TestIntDelegate(int g);
class TestClass
{
static void Main()
{
// 传递Lambda表达式
Test(y => y * y);
}
public void Test(TestIntDelegate ti)
{
int area = ti(10);
}
}
a.两者参数个数相同
b.参数类型相同,注意隐式类型要参与类型辨析
c.委托的返回类型要与Lambda的相同,不论是表达式还是语句块
4.Lambda表达式与表达式树
表达式树典型的声明形式是
1: Func<int, int> func = input => input * input;
2: Expression<Func<int, int>> expression = input => input * input;
这里需要System.Linq.Expressions命名空间中的Expression<T>,而这个T是定义表达式签名的委托的类型;看看表达式树完整的使用代码:2: Expression<Func<int, int>> expression = input => input * input;
1: static void Main(string[] args)
2: {
3: Func<int, int> func = input => input * input;
4: Console.WriteLine(func(3).ToString());
5:
6: Expression<Func<int, int>> expression = input => input * input;
7: Func<int, int> fun = expression.Compile();
8: Console.WriteLine(fun(5).ToString());
9:
10: Console.ReadLine();
11: }
使用表达式树:
a. 表达式,可以转换他们成为委托并调用它们。
b.转换表达式到SQL,那将使得它们可被服务器执行(这就是LINQ to SQL 做的)
c.转换表达式到XML并写入它们到磁盘上。 转换表达式到自定义格式,可以通过网络协议放松到服务器,并可以重新建立表达式树,之后根据它执行与/或(And/OR)。
通过利用由Lambda提供的对表达式树的支持,以及 IQueryable<T> 接口,构建数据提供器的框架开发人员可以确保开发人员编写干净的编码,对任何数据源(无论是数据库,XML文件,内存中的对象,web服务,LDAP系统等)运行起来速度快而且效率高。
5.小结
一般匿名方法会使用Lambda表达式替换,并且在语句较少功能较简单的时候才只用Lambda表达式,否则写出来的代码在可读性方面可能会比较差,为以后的维护和修改工作带来不必要的麻烦。在Linq中Lambda会大量使用,随着学习的逐渐深入,对Lambda表达式以及表达式树会不断补充与修正。
参考:匿名函数(C# 编程指南)
Lambda 表达式(C# 编程指南)
匿名方法(C# 编程指南)
表达式与表达式树参考:
http://blog.joycode.com/scottgu/archive/2007/04/09/100744.aspx
http://www.cnblogs.com/GSonOVB/archive/2008/08/29/1279315.html
http://www.cnblogs.com/Autumoon/archive/2007/11/20/964996.html