一、匿名方法
1.匿名方法的由來
• 沒有匿名方法的時候(C# 1.0):
addButton.Click += new EventHandler (AddClick) ;
void AddClick(object sender, EventArgs e) {
listBox.Items.Add(textBox.Text);
}
• 有了匿名方法之後(C# 2.0):
addButton.Click += delegate {
listBox.Items.Add(textBox.Text);
};
2.匿名方法簡介
• 匿名方法允許我們以一種“內聯”的方式來編寫方法代碼,將代碼直接與委託實例相關聯,從而使得委託實例化的工作更加直觀和方便。
• 匿名方法的幾個相關問題:
– 參數列表
– 返回值
– 外部變量
2.1.匿名方法的參數
• 匿名方法可以在delegate關鍵字後跟一個參數列表(可以不指定),後面的代碼塊則可以訪問這些參數:
addButton.Click +=delegate(object sender, EventArgs e) {
MessageBox.Show(((Button)sender).Text);
};
• 注意“不指定參數列表”與“參數列表爲空”的區別
addButton.Click += delegate {…} // 正確!
addButton.Click += delegate() {…} // 錯誤!
2.2匿名方法的返回值
• 如果委託類型的返回值類型爲void,匿名方法裏便不能返回任何值;
• 如果委託類型的返回值類型不爲void,匿名方法裏返回的值必須和委託類型的返回值兼容:
delegate int MyDelegate();
MyDelegate d = delegate{
……
return 100;
};
delegate void MyDelegate();
MyDelegate d = delegate{
……
return;
};
2.3匿名方法的外部變量
• 一些局部變量和參數有可能被匿名方法所使用,它們被稱爲“匿名方法的外部變量”。
• 外部變量的生存期會由於“匿名方法的捕獲效益”而延長——一直延長到委託實例不被引用爲止。
static void Foo(double factor) {
Function f=delegate(int x) {
factor +=0. 2; // factor爲外部變量
return x * factor;
};
Invoke(f); // factor的生存期被延長
}
3.委託類型的推斷
• C#2.0 允許我們在進行委託實例化時,省略掉委託類型,而直接採用方法名,C#編譯器會做合理的推斷。
• C# 1.0中的做法:
addButton.Click += new EventHandler(AddClick);
Apply(a, new Function(Math.Sin));
• C# 2.0中的做法:
addButton.Click += AddClick;
Apply(a, Math.Sin);
4.匿名方法機制
• C# 2.0中的匿名方法僅僅是通過編譯器的一層額外處理,來簡化委託實例化的工作。它與C# 1.0的代碼不存在根本性的差別。
• 通過ILDasm.exe反彙編工具,我們可以獲得對匿名方法的深入瞭解:
– 靜態方法中的匿名方法
– 實例方法中的匿名方法
– 匿名方法中的外部變量
靜態方法中的匿名方法
public delegate void D();
static void F() {
D d = delegate { Console.WriteLine("test"); };
}
}
上面的代碼被編譯器轉換爲:
static void F() {
D d = new D(__Method1);
}
static void __Method1() {
Console.WriteLine("test");
}
實例方法中的匿名方法
class Test {
int x;
void F() {
D d = delegate { Console.WriteLine(this.x); };
}
}
上面的代碼被編譯器轉換爲:
void F() {
D d = new D(__Method1);
}
void __Method1() {
Console.WriteLine(this.x);
}
匿名方法中的外部變量
void F() {
int y = 123;
D d = delegate { Console.WriteLine(y); };
}
上面的代碼被編譯器轉換爲:
class __Temp
{
public int y;
public void __Method1() {
Console.WriteLine(y);
}
}
void F() {
__Temp t = new __Temp();
t.y = 123;
D d = new
D(t.__Method1);
}
二、迭代器
1.C# 1.0中的foreach
• 沒有迭代器的時候,創建一個可用於foreach的集合(C# 1.0):
public class MyCollection : IEnumerable {
public MyEnumerator GetEnumerator() {
return new MyEnumerator(this);
}
public class MyEnumerator : IEnumerator {
public void Reset(){… }
public bool MoveNext(){ … }
public int Current{ get{ … } }
object IEnumerator.Current { get{ … } }
}
}
• 對集合使用foreach語句:
foreach (int i in col) { ….. }
• 相當於:
2.C#中的迭代器
IEnumerator etor =
((IEnumerable)col).GetEnumerator();
try {
while (etor.MoveNext()) {
ElementType elem = (ElementType)etor.Current;
…….;
}
}
finally { ((IDisposable ) enumerator). Dispose(); }
• 使用迭代器創建用於foreach的集合(C# 2.0):
public class Stack<T>: IEnumerable<T>
{
T[] items;
int count;
public void Push(T data) {...}
public T Pop() {...}
public IEnumerator<T> GetEnumerator() {
for (int i = count – 1; i >= 0; --i) {
yield return items[i];
}
}
}
• 使用foreach語句:
Stack<int> stack = new Stack<int>();
foreach (int i in stack) { ……. }
• 使用迭代器創建倒序遍歷:
public IEnumerable<T> BottomToTop {
get {
for (int i = 0; i < count; i++) {
yield return items[i];
}
}}
3.迭代器中的yield語句
• 使用yield return 產生枚舉元素:
for (int i = count – 1; i >= 0; --i) {
yield return items[i];
}
• 使用yield break 中斷迭代:
for (int i = count – 1; i >= 0; --i) {
yield return items[i];
if (items[i]>10)
yield break;
}
4.迭代器機制
• C# 2.0中的迭代器同樣是通過編譯器的一層額外處理,來簡化創建可用於foreach的枚舉集合的工作。
• 通過ILDasm.exe反彙編工具,我們可以獲得對迭代器的深入理解:
– 迭代器中的GetEnumerator()方法
– 迭代器中的嵌套類
– 迭代器中的yield語句
使用ILDasm剖析迭代器機制
總結
• 匿名方法允許我們以一種“內聯”的方式將代碼直接與委託實例相關聯,從而使得委託實例化的工作更加直觀和方便。迭代器允許我們更加方便地編寫應用於foreach語句的枚舉集合。
• 對於類似於C#這樣的高級語言,掌握好它一個很重要的地方就是掌握編譯器在背後爲我們做了哪些工作。C# 2.0中的匿名方法和迭代器都是通過在編譯層面進行一些額外的處理,進而來簡化程序員的工作。