【C#】详解C#委托
目錄結構:
contents structure [+]
1.委托語法
本文會詳細闡述委托的使用,以及實現,想必讀者已經知道了函數編程中的回調機制,C#為回調機制提供了一種簡便語法,這就是委托。
在使用委托之前需要使用delegate關鍵字進行聲明:
比如,
上面定義了一個無參數,無返回的委托。定義好后,然后就可以聲明委托對象。
MyDelegate myDelegate=new MyDelegate(SomeStaticMethod);上面創建了一個委托對象,這個委托對象關聯了一個方法參數。
C#為這種操作提供了一種糖語法,就是不需要構造委托對象,如下:
示例:
?
2.泛型委托
C#允許泛型委托,目的是保證任何類型的對象都可以以類型安全的方式傳給回調方法。
例如:
委托的每個泛型類型參數都可以標記為協變量和逆變量。
在這里介紹一下泛型類型參數的三種變量:
不變量(invariant):意味著泛型類類型參數不能更改。
逆變量(contravariant):意味著泛型類型參數可以從一個類更改為它的某個派生類,C#中使用in關鍵字標記逆變量形式的泛型類型參數。逆變量泛型類型參數T只能出現在輸入位置,作為參數。
協變量(convariant):意味著泛型類型參數可以從一個類更改為它的某個基類,C#中使用out關鍵字標記協變量形式的泛型類型參數。逆變量泛型類型參數只能出現在輸出位置,作為返回類型。
例如存在以下泛型類型委托:
如果像下面定義一個委托變量:
MyFunc(Object,ArgumentException) fn1=null;然后就可以將它轉變為其他委托類型變量:
MyFunc(String,Exception) fn2=fn1;//轉化 fn2("");//調用3.委托鏈
C#中提供了一種委托鏈的操作,通過委托鏈可以關聯多個方法。
C#在實現委托鏈的時候使用了組合設計模式。C#中使用Delegate類的Combine類來完成委托的鏈接,例如:
上面我們使用Delegate的Combine方法合并為委托鏈,其實Delegate類還提供了許多操作方法,例如:
Delegate.remove(Delegate,Delegate);//從第一個委托鏈中移除第二個委托鏈中的委托 Delegate.DynamicInvoke(Object[]);//調用委托鏈中的所有方法 static Delegate CreateDelegate(Type type,MethodInfo method)//從給定的委托類型中,創建一個靜態方法委托在C#中還給Delegate重載了+=和-=操作符,可以更方便的操作委托鏈,比如:
Max max1 = new Max(new Program().Method1);//定義委托max1Max max2 = new Max(new Program().Method2);//定義委托max2max1 += max2;4.lambda表達式
Lambda表達式是C#3.0才添加的功能,是為委托添加的糖語法,lambda表達式允許以內聯的方式寫回調方法的代碼,而不需要單獨寫到方法中,例如:
delegate Int32 Max(Int32 a, Int32 b);static void Main(string[] args){Max m = null;m += (a, b) => {return a > b ? a : b;};//lambda表達式Int32 res= m(10,11);//調用方法 Console.WriteLine(res);Console.ReadLine();}lambda表達式的大致格式為:(參數...)=>{方法主體}
通常我們在寫線程的時候,我們都會定義一個WaitCallback委托,然后再關聯方法,現在我們可以像如下這樣:
關于使用委托,這里有一個不成文規定,通常若是需要在回調方法寫3行以上的代碼就不用lambda表達式,而是重新定義一個方法,并為其分配名稱。若小于等于3行代碼,就可以使用lambda表達式。
例如:
//創建并初始化一個String數組String[] names = { "Jeff","Kristin","Aidan","Grant"};//獲取只含有小寫字母a的名稱Char charTOFind='a';names = Array.FindAll(names, (name) => { return name.IndexOf(charTOFind) >= 0; });//將每個字符串的名稱轉化為大寫names= Array.ConvertAll(names, (name) => { return name.ToUpper(); });//打印Array.ForEach(names,System.Console.WriteLine);5.揭秘委托
到現在我們已經知道了如何定義委托,以及C#為委托提供的糖語法,接下來我們開始探討一下委托究竟是什么,將如下的代碼編譯為二進制文件,
class Program{delegate Int32 MathAdd(Int32 a, Int32 b);static void Main(string[] args){MathAdd mathAdd = new MathAdd((Int32 a, Int32 b) => { return a + b; });Int32 result= mathAdd(3,2);Console.WriteLine(result);//5 Console.ReadKey();}}然后我們使用ildasm.exe反編譯應用程序得到il文件,就可以從CLR層面觀察出C#的委托到底是怎么回事了。
通過這張圖片我們可以清晰的觀察出,IL代碼生成了一個靜態內部類MathAdd,然后該類派生于MulticastDelegate,到現在我們清楚了C#的委托本質上就是類。除此之外,該類提供了一個構造器方法,BeginInvoke方法,EndInvoke方法,Invoke方法。
6.類庫中的委托
C#已經預先定義好了豐富的委托類型供開發人員使用,在MSCorLib.dll中,有將近50個委托類型,
例如:
......
上面這些委托都有一個共同點,就是他們都是一樣的,也就是說,只需要定義一個委托就可以了。
.NET Framework支持泛型,C#已經為我們定義好了17個Action委托,其中包含16個泛型委托:
除了Action委托,C#還為我們定義了Func委托,允許回調方法返回值:
public delegate TResult Func<TResult>(); public delegate TResult Func<T,TResult>(T arg); public delegate TResult Func<T1,T2,TResult>(T1 arg1,T2 arg2); public delegate TResult Func<T1,T2,T3,TResult>(T1 arg1,T2 arg2,T3 arg3); ...... public delegate TResult Func<T1,.....,T16,TResult>(T1 arg1,.....,T16 arg16);上面列出了C#定義的Action和Func委托,如果涉及到委托最好是使用這些委托類型,而不是重新定義,這樣可以減少程序集的大小。
但是如果涉及到ref或out參數,那么就只有自己定義了。
比如:
7.委托和反射
通常情況下,使用委托都應該知道委托的原型,但是反射提供了另一種可能來使用委托。
在MethodInfo中提供了一個createDelegate方法,運行在編譯器不知道委托的所有信息下提前創建委托。
在創建好Delegate對象后,就可以通過調用對象的DynamicInvoke方法來調用委托。
public Object DynamicInvoke(params Object[] args);下面的展示了如何使用反射來調用委托:
class Program{static void Main(string[] args){//定義一個Func<Int32,Int32,Int32>類型的數據Type type=typeof(Func<Int32,Int32,Int32>);//獲得Program實例的Max方法MethodInfo methodInfo = typeof(Program).GetMethod("Max", BindingFlags.Instance|BindingFlags.Public);//創建委托Delegate d = methodInfo.CreateDelegate(type, new Program());//調用Object res = d.DynamicInvoke(13, 12);Console.WriteLine(res);//13 Console.ReadKey();}public Int32 Max(Int32 a, Int32 b){return a > b ? a : b;}}?
轉載于:https://www.cnblogs.com/HDK2016/p/7771404.html
總結
以上是生活随笔為你收集整理的【C#】详解C#委托的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux下安装oracle客户端,实现
- 下一篇: 信安协会作业2