前言
簡單介紹一下什麼是委託。
正文
以前也寫過委託,這次算是重新歸檔,和新的補充吧。
https://www.cnblogs.com/aoximin/p/13940125.html
有些人說委託是函數指標的包裝,也有些人說委託是一個方法或多個方法的參照。
這都是沒有問題,委託是一個概念,微軟官方檔案說委託是一種參照型別,表示對具有特定參數列和返回型別的方法參照。
我覺得太囉嗦了,實際上就是方法的參照。
上面都是委託的概念,但是實現方式每種語言可能都不一樣。
比如c++ 和 c 用的是函數指標,而c# 用的是生成包裝類(等下IL),當然本質還是函數指標。
那麼來看下委託。
internal class Program
{
delegate int TestDelegate(int a);
static void Main(string[] args)
{
TestDelegate a = test;
a(0);
}
public static int test(int a)
{
return 0;
}
}
將test 給了委託a,然後呼叫的時候直接a()就可以了。
用起來十分簡單。
實際上對IL來說其實是沒有委託這個概念的,通過反編譯來看下原理。
private static void Main(string[] args)
{
TestDelegate a = new TestDelegate(test);
a(0);
}
實際上會生成TestDelegate這樣一個類,然後將test 參照新增進去。
來看下il。
再看下TestDelegate是一個什麼樣的類。
就是把呼叫的object 和 方法的參照放入包裝類中了,然後invoke 可以進行呼叫。
如果是多個方法的參照呢?
internal class Program
{
delegate int TestDelegate(int a);
static void Main(string[] args)
{
TestDelegate a = test;
a += test;
a(0);
}
public static int test(int a)
{
return 0;
}
}
看下IL:
原理就是又new了一個TestDelegate,然後用Delegate 將兩個相連。
Combine 是一個靜態方法哈。
本質是呼叫a的combineImp這個方法。之所以有這個一個靜態方法是為了避免出現a為空的情況,如果a為空,直接把b給a啊。
這個是我們寫鏈式結構可以學習的,這樣就不用判斷宣告的時候是否為空。
然後c# 幫我們提取定義了很多委託,以至於我們幾乎不用去宣告委託。
比如Func 和 Action,Func 有返回值,Action沒有。
下面介紹匿名函數,匿名函數有兩個要介紹的,他們分別是匿名方法和lambda表示式。
他們原理都一樣,都是生成匿名函數,只是寫法不一樣。
delegate int TestDelegate(int a);
static void Main(string[] args)
{
TestDelegate a = delegate (int a)
{
return 0;
};
}
看下反編譯後的內容。
private static void Main(string[] args)
{
TestDelegate a = <>c.<>9__1_0 ?? (<>c.<>9__1_0 = new TestDelegate(<>c.<>9.<Main>b__1_0));
}
那麼看下<>c 這個類:
首先看到第一個框,那麼作者的意思是想把 <>c做成一個單例。
裡面有委託的參照。然後下面這個
b__1_0 就是生成的方法。
其實匿名方法還是編譯幫忙生成對應的方法名。
如果用lambda 表示式寫的話,那麼是這樣寫的:
這種寫法編譯出來的程式碼一模一樣。只是不同寫法的問題。
值得注意的是匿名函數如果參照了外部的資訊,那麼會形成閉包。
比如說:
static void Main(string[] args)
{
Student s = new Student();
TestDelegate a = (a) => {
s = null;
return 0;
};
a += (b) => {
return 0;
};
a += (c) => {
return 0;
};
}
首先b和c(第二個和第三個匿名)沒有參照外部物件,那麼都會生成在<>c這個類中。
第一個有外部參照生成了另外一個類。
然後範例化<>c__DisplayClass1_0後,那麼會將s賦值進來。
所以會形成這種閉包,這是值得注意的地方。
結
下一節委託的釋出訂閱與事件。