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);