標籤:c# .net 學習 委託 delegate
大家好,這是本系列的第二篇文章,今天我給大家帶來的是C#當中委託部分。
那麼先講講委託吧,委託是一種定義方法簽名的類型,當執行個體化委託時,您可以將其執行個體與任何具有相容簽名的方法相關聯。 您可以通過委託執行個體調用方法。
使用委託的一個好處就是像操作變數一樣操作一個方法。
下面是委託的一些特點:
- 委託類似於 C++ 函數指標,但它們是型別安全的。
- 委託允許將方法作為參數進行傳遞。
- 委託可用於定義回調方法。
- 委託可以連結在一起;例如,可以對一個事件調用多個方法。
- 方法不必與委託簽名完全符合。
下面 是定義個名為MyDelegate的委託,其參數類型為object,返回值為void
public delegate void MyDelegate(object obj);
這行代碼就是用來聲明一個委託類型。再看如下代碼:
class Program { public delegate void MyDelegate(object obj); static void Main(string[] args) { // 在這兒,把MyDelegate看做一個類(其實它就是一個類class) // 下面這行代碼和聲明一個對象一模一樣,因為mydelegate它就是一個對象 // 注意Method1的原型和MyDelegte一致才行(逆變和協變的內容在後面) // 其實下面這行代碼可以簡寫為:MyDelegate mydelegate = Method1; MyDelegate mydelegate = new MyDelegate(Method1); // 下面這行代碼和mydelegate.Invoke(new object());沒有任何區別! // 這行代碼的目的就是執行Method1方法 mydelegate(new object()); } // 方法 i public static void Method1(object o) { Console.WriteLine("Method1 Calling"); } }
運行輸入結果:Method1 Calling.
那麼委託時如何像一個參數一樣傳給別的方法呢?下面這段代碼用委託回調的方式開求1~100的和:
class Program { public delegate void CallBack(int number); static void Main(string[] args) { // 這個函數調用後,將輸出1~100的和 Execute(100, Sum); } private static void Execute(int param, CallBack callBack) { if (callBack != null) { // 在這個地方調用callBack委託 callBack(param); } } private static void Sum(int number) { int sum = 0; for (int i = 0; i <= number; i++) { sum += i; } // 為了做示範,就暫且用這種最複雜的方法求和吧 Console.WriteLine("1~" + number + "的和為" + sum); } }
程式運行結果:1~100的和為5050。
那麼就有人要說了,我像調用一次Execute方法,求出1~100的和之外還求出他們的所有質數之和怎麼做呢,那麼就可以使用委託將多個方法連結到一起了:
class Program { public delegate void CallBack(int number); static void Main(string[] args) { // 這個函數調用後,將輸出1~100的和還有1~100之間質數的和 CallBack linkDelegate = Sum; // 使用+=運算子,將2個方法連結一起 linkDelegate += SumOfPrimeNumber; Execute(100, linkDelegate); } private static void Execute(int param, CallBack callBack) { if (callBack != null) { // 在這個地方調用callBack委託 callBack(param); } } private static void Sum(int number) { int sum = 0; for (int i = 0; i <= number; i++) { sum += i; } // 為了做示範,就暫且用這種最複雜的方法求和吧 Console.WriteLine("1~" + number + "的和為" + sum); } private static void SumOfPrimeNumber(int number) { int sum = 0; for (int i = 0; i <= number; i++) { if (IsPrime(i)) { sum += i; } } Console.WriteLine("1~" + number + "之間的所有質數的和為" + sum); } // 判斷一個數字是否為質數 private static bool IsPrime(int n) { for (int i = 2; i * i <= n; i++) { if (n % i == 0) { return false; } } return true; } }
這段代碼輸出的結果為:1~100的和為5050。1~100之間的所有質數的和為1061。
下面就來講講委託的2個重點:逆變和協變。
- 逆變:在使用委託中,可以將方法參數從他基類修改為他的衍生類別。
- 協變:在使用委託中,可以將方法返回值從他衍生類別修改為他的基類。
下面是一個逆變的例子:
class Program { public delegate void Contravariance(string str); static void Main(string[] args) { Contravariance md = Method1; md("Contravariance"); } static void Method1(object obj) { Console.WriteLine(obj + " Method1 Calling"); } }
儘管Method1的原型和Contravariance委託定義的不一致,但是對於參數而言,Method1中的object是Contravariance中的string的基類,所以上述代碼還是能正常運行,這個就是逆變。
不過得注意下面這個例子:
class Program { public delegate void Contravariance(int str); static void Main(string[] args) { // 注意下面這行代碼,雖然int是object的衍生類別型,但是下面這行代碼還是會導致編譯不過,因為逆變和協變對實值型別不起作用(int為實值型別) Contravariance md = Method1; md(4); } static void Method1(object obj) { Console.WriteLine(obj + " Method1 Calling"); } }上述代碼是無法通過編譯的,在使用逆變和協變中是都不支援值類型的。
同理,下面這個是逆變的例子:
class Program { public delegate object Convariance(); static void Main(string[] args) { // 雖然Convariance委託定義的類型傳回值和Method1不一致,但是這段代碼是可以正常啟動並執行 Convariance md = Method1; md(); } static string Method1() { return "Method1"; } }
我們看一看如下代碼:
public delegate void TryCode(object userData); public delegate void WaitCallback(object state); public delegate void TimerCallback(object state); public delegate void ParameterizedThreadStart(object obj);
發現這些委託定義的共同點了嗎?是不是都一模一樣?事實上在.NET Framework上可以使用委託泛型,上面所有的泛型其實都可以僅使用:
public delegate void Action<in T>(T obj);
這一個委託定義來實現就可以了。在System命名空間下有這種多達17個Action委託(這兒使用in關鍵字表示委託類型支援逆變):
public delegate void Action(); public delegate void Action<in T1>(T1 arg1); public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2); ...
如果是需要有參數的委託的話,可以使用如下委託:
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
同樣的System命名空間下也有17個Func的委託(使用out關鍵字表示委託泛型支援協變):
public delegate TResult Func<out TResult>(); public delegate TResult Func<in T1, out TResult>(T1 arg1); public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); ...
最後是文本的關鍵點,為大家揭秘委託。
我們再看如下一行代碼:
internal delegate void Feedback(int value);
其實它被編譯後,看起來像如下一個完整的類:
internal class Feedback : System.MulticastDelegate { // 構造方法 public Feedback(object obj, IntPtr method); // 這個方法的原型和委託定義的一模一樣 public virtual void Invoke(int value); // 一下方法實現了對回調方法的非同步呼叫 public virtual IAsyncResult BeginInvoke(int value, AsyncCallback callback, Object obj); public virtual void EndInvoke(IAsyncResult result); }
也就是說其實委託它本身也是一個類,其繼承關係為:自訂委託->MulticastDelegate->Delegate->Object。
本文內容就講到這裡了,謝謝各位。
C#學習之步步高(二)認識到熟悉委託