c#2.0介绍了一个新特性--匿名方法,允许开发者在线(inline)声明自己的函数代码而无须使用委托函数(delegate function)。c#3.0中提供了一个新特性--lambda表达式,它提供了完成相同目标的更加简洁的格式。让我们在讨论lambda表达式以前仔细研究一下匿名方法。
匿名方法
假设你需要创建一个按钮,当点击它的时候更新listbox里的内容。在c#1.0和1.1里,你要这样做:
public myform()
{
listbox = new listbox(...);
textbox = new textbox(...);
addbutton = new button(...);
addbutton.click += new eventhandler(addclick);
}
void addclick(object sender, eventargs e)
{
listbox.items.add(textbox.text);
}
在c#2.0里,你需要这样做:
public myform()
{
listbox = new listbox(...);
textbox = new textbox(...);
addbutton = new button(...);
addbutton.click += delegate
{
listbox.items.add(textbox.text);
};
就像你看到的一样,你不必要特别的声明一个新方法来将它连接到一个事件上。你可以在c#2.0里使用匿名方法来完成同样的工作。c#3.0里介绍了一种更加简单的格式,lambda表达式,你可以直接使用"=>"来书写你的表达式列表,后面跟上一个表达式或者语句块。
lambda表达式中的参数
lambda表达式中的参数可以是显式或者隐式类型的。在一个显式类型参数列表里,每个表达式的类型是显式指定的。在一个隐式类型参数列表里,类型是通过上下文推断出来的:
(int x) => x + 1 // 显式类型参数
(y,z) => return y * z; // 隐式类型参数
lambda演算实例
下面的例子给出了两种不同的方法来打印出一个list中长度为偶数的字符串。第一种方法anonmethod使用了匿名方法,第二种lambdaexample则是通过lambda演算实现:
// program.cs
using system;
using system.collections.generic;
using system.text;
using system.query;
using system.xml.xlinq;
using system.data.dlinq;
namespace lambdaexample
{
public delegate bool keyvaluefilter<k, v>(k key, v value);
static class program
{
static void main(string[] args)
{
list<string> list = new list<string>();
list.add("aa");
list.add("abc");
list.add("defg");
list.add("xyz");
console.writeline("through anonymous method");
anonmethod(list);
console.writeline("through lambda expression");
lambdaexample(list);
dictionary<string, int> varclothes= new dictionary<string,int>();
varclothes.add("jeans", 20);
varclothes.add("shirts", 15);
varclothes.add("pajamas", 9);
varclothes.add("shoes", 9);
var clotheslistshortage = varclothes.filterby((string name,
int count) => name == "shoes" && count < 10);
// example of multiple parameters
if(clotheslistshortage.count > 0)
console.writeline("we are short of shoes");
console.readline();
}
static void anonmethod(list<string> list)
{
list<string> evennumbers = list.findall(delegate(string i)
{ return (i.length % 2) == 0; });
foreach (string evennumber in evennumbers)
{
console.writeline(evennumber);
}
}
static void lambdaexample(list<string> list)
{
var evennumbers = list.findall(i =>(i.length % 2) == 0); // example of single parameter
foreach(string i in evennumbers)
{
console.writeline(i);
}
}
}
public static class extensions
{
public static dictionary<k, v> filterby<k, v>
(this dictionary<k, v> items, keyvaluefilter<k, v> filter)
{
var result = new dictionary<k, v>();
foreach(keyvaluepair<k, v> element in items)
{
if (filter(element.key, element.value))
result.add(element.key, element.value);
}
return result;
}
}
}
如果你安装了visual studio 2005 and linq preview,你可以使用编辑器来编译程序。如果没有的话,可以使用命令行方式:
c:/program files/linq preview/bin/csc.exe
/reference:"c:/program files/linq preview/bin/system.data.dlinq.dll"
/reference:c:/windows/microsoft.net/framework/v2.0.50727/system.data.dll
/reference:c:/windows/microsoft.net/framework/v2.0.50727/system.dll
/reference:"c:/program files/linq preview/bin/system.query.dll"
/reference:c:/windows/microsoft.net/framework/v2.0.50727/system.xml.dll
/reference:"c:/program files/linq preview/bin/system.xml.xlinq.dll"
/target:exe program.cs
中间语言结果显示
双击anonmethod函数你将看到c#编译器产生的中间语言代码:
.method private hidebysig static void anonmethod(class
[mscorlib]system.collections.generic.list`1<string> list)
cil managed
{
// code size 96 (0x60)
.maxstack 4
.locals init ([0] class [mscorlib]system.collections.generic.list
`1<string> evennumbers,
[1] string evennumber,
[2] valuetype [mscorlib]system.collections.generic.list
`1/enumerator<string> cscode_replacement 000,
[3] bool cscode_replacement 001)
il_0000: nop
il_0001: ldarg.0
il_0002: ldsfld class [mscorlib]system.predicate
`1<string> lambdaexample.program::
`<>9__cachedanonymousmethoddelegate1'
il_0007: brtrue.s il_001c
il_0009: ldnull
il_000a: ldftn bool lambdaexample.program::
`<anonmethod>b__0'(string)
il_0010: newobj instance void class [mscorlib]system.predicate
`1<string>::.ctor(object, native int)
il_0015: stsfld class [mscorlib]system.predicate`1<string>
lambdaexample.program::
`<>9__cachedanonymousmethoddelegate1'
il_001a: br.s il_001c
il_001c: ldsfld class [mscorlib]system.predicate`1<string>
lambdaexample.program::'<>
9__cachedanonymousmethoddelegate1'
il_0021: callvirt instance class [mscorlib]system.collections.
generic.list`1<!0> class [mscorlib]system.
collections.generic.list`1<string>::
findall(class [mscorlib]system.predicate`1<!0>)
il_0026: stloc.0
il_0027: nop
il_0028: ldloc.0
il_0029: callvirt instance valuetype [mscorlib]system.collections.
generic.list`1/enumerator<!0> class
[mscorlib]system.collections.generic.list`1
<string>::getenumerator()
il_002e: stloc.2
.try
{
il_002f: br.s il_0042
il_0031: ldloca.s cscode_replacement 000
il_0033: call instance !0 valuetype [mscorlib]system.
collections.generic.list`1/enumerator
<string>::get_current()
il_0038: stloc.1
il_0039: nop
il_003a: ldloc.1
il_003b: call void [mscorlib]system.console::
writeline(string)
il_0040: nop
il_0041: nop
il_0042: ldloca.s cscode_replacement 000
il_0044: call instance bool valuetype [mscorlib]system.
collections.generic.list`1/enumerator
<string>::movenext()
il_0049: stloc.3
il_004a: ldloc.3
il_004b: brtrue.s il_0031
il_004d: leave.s il_005e
} // end .try
finally
{
il_004f: ldloca.s cscode_replacement 000
il_0051: constrained. valuetype [mscorlib]system.collections.
generic.list`1/enumerator<string>
il_0057: callvirt instance void [mscorlib]system.
idisposable::dispose()
il_005c: nop
il_005d: endfinally
} // end handler
il_005e: nop
il_005f: ret
} // end of method program::anonmethod
这里我们可以看到,实际上匿名方法和lambda表达式生成了相同的中间代码,并且他们的执行也是类似的。
多参数的lambda表达式
lambda表达式可以带上多个参数,比如你可以声明一个dictionary类型:
clothing type | count |
shirts | 15 |
jeans | 12 |
shoes | 9 |
pajamas | 9 |
如果你有一个匿名方法(filterby)来通过键和值来过滤字典,按么你可以传递多个参数给lambda表达式来调用这个匿名方法。附带的代码完成了这个filterby的功能:var clotheslistshortage = clotheslist.filterby((string name, int count)
=> name == "shoes" && count < 10);