做一個決定,並不難,難的是付諸行動,並且堅持到底 --Aaronyang的博客(www.ayjs.net)-www.8mi.me
1. 委托-我的總結
1.1 委托:面試我都會說,把方法當參數。委托包含的只是一個或多個方法的地址。
示例1:(一次執行多個同方法簽名的方法)
/* *2015年1月3日23:12:13 aaronyang *網址:www.ayjs.net www.8mi.me */ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace demo0103 { delegate void TotalMethod(int type);//第一步delegate 方法簽名 定義個方法的樣子,如同接口 class Program { static void Main(string[] args) { //TotalMethod tm =new TotalMethod(Method1);//或者 非常建議 直接 +=或者= TotalMethod tm = Method1 ; tm += Method2; tm += Method3; tm(2);//一次性執行3個方法
tm.Invoke(3);//或者使用Invoke方法
Console.ReadLine(); } static void Method1(int t) { Console.WriteLine("Method1:"+t*10+"\t"); } static void Method2(int t) { Console.WriteLine("Method2:" + t * 100 + "\t"); } static void Method3(int t) { Console.WriteLine("Method3:" + t * 1000 + "\t"); } } }
效果:
示例2:委托類型的數組。把委托當做參數,這個技巧一定要掌握,可以寫出很精彩的代碼,很像Javascript傳方法。方法的Lambda表達式寫法初次見面
/* *2015年1月3日23:12:13 aaronyang *網址:www.ayjs.net www.8mi.me */ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace demo0103 { delegate void TotalMethod(ref int type);//第一步delegate 方法簽名 定義個方法的樣子,如同接口 /// <summary> /// 處理TotalMethod方法,將轉換后的數字,按照count數進行一定同樣的數字相乘 /// 我沒有定義一個和此委托方法簽名一致的方法,使用的是Lambda表達式定義一個方法 /// </summary> /// <param name="total"></param> /// <param name="count"></param> /// <returns></returns> delegate int ProcessTotalMethod(TotalMethod total,int count); class Program { static void Main(string[] args) { //TotalMethod tm =new TotalMethod(Method1);//或者 非常建議 直接 +=或者= //TotalMethod tm = Method1 ; // tm += Method2; // tm += Method3; //tm(2);//一次性執行3個方法 //tm.Invoke(3);//或者使用Invoke方法 TotalMethod[] tms = { Method1, Method2, Method3 }; //委托數組 int initTypeNum=2; ProcessTotalMethod ptm = (x,y) => { x(ref initTypeNum);//將init按照10,100,1000倍相乘,並返回 Console.WriteLine("乘以倍數后的數字:" + initTypeNum ); //接下來連續乘以y個 initTypeNum for (int i = 0; i < y; i++) { initTypeNum *= initTypeNum; } return initTypeNum; }; Console.WriteLine("終於連續2次相乘后的數字:" + ptm(tms[0], 2)); Console.ReadLine(); } static void Method1(ref int t) { t = t * 10; Console.WriteLine("Method1:"+t+"\t"); } static void Method2(ref int t) { t = t * 100; Console.WriteLine("Method2:" + t + "\t"); } static void Method3(ref int t) { t = t * 1000; Console.WriteLine("Method3:" + t + "\t"); } } }
效果:
1.2 框架自帶好的常用的 Action<T1,[T2]...[T8]>和Func<T1,T2..[T8]> 可查看我寫的Func教程:查看
Action 是個 void的委托,其中T1,T2..T8都是方法的參數,而Func是個,最后一個泛型參數是返回值,前面是方法的參數,例如Func<int,string,double> 那么最后一個double就是返回值類型,前面是方法簽名的參數,等同於定義了
delegate double Method1(int,string);
關於Func可以寫出很多很精彩的代碼,真的很期待大家的發揮。
1.3 多播委托
如1.1示例1講解的就是一個多播委托的簡單的例子。
aaronyang的認識: 一個委托綁定了多個方法,然后執行委托,如果委托定義的方法簽名是void,則依次執行,如果報錯,迭代就停止。如果委托定義的方法簽名有返回值,例如像int,string等類型,就只會看到最后一個結果。委托可以使用+=增加方法調用,使用
-=刪除方法調用。
接下來,演示個Delegate類的GetInvocationList()方法返回一個Delegate數組
1.4 匿名方法其實在1.3我們已經見過了,直接 (方法參數)=>{ 方法體} 來簡易的 定義一個方法類型。如果只有一個方法參數,括號可以去掉,例如 x=>{方法體}
也可以稍微煩一點的寫法, 增加一個delegate關鍵字,例如 Func<string,string> a=delegate(string s){ return s} 也等同於 Func<string,string> a = s=>{return s} 也等同於 Func<string,string> a = s =>"修飾后:"+s;
1.5 閉包:Lambda表達式可以訪問Lambda表達式外的變量;可能問題:假如多線程環境,修改了lambdawai的變量值,會使結果可能不正確了。
int lambdawai = 5; Func<int,int> f = x => x+lambdawai; Console.WriteLine(lambdawai); // 輸出 5; Console.WriteLine(f(3)); // 輸出 8;
*1.6 C# 5.0很大的改變,foreach語句的閉包 參考文章:文章
個人小試:Win8.1 沒發現。。問題
案例:下面代碼輸出什么,C#4.0 輸出5,5,5,5,5 C#5.0輸出1,2,3,4,5
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); foreach (int x in data) { actions.Add(() => x); } foreach (var foo in actions) { Console.WriteLine(foo()); }
C# 4.0 foreach執行原理:的確輸出5個5
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); IEnumerator e = data.GetEnumerator(); int x = 0; while (e.MoveNext()) { x = (int)e.Current; actions.Add(() => x); } foreach (var foo in actions) { Console.WriteLine(foo()); }
注意迭代變量x是在循環塊外部被定義的。
這里涉及到一個很重要的概念,閉包,在Lambda表達式中,我們使用了外層的自由變量x,注意,在調用lambda表達式的時候,x會被求值,而這個定義在外部的x變量在循環終了等於5,這是為什么都是輸出5的原因
C# 5.0 foreach執行原理:的確 1,2,3,4,5
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); IEnumerator e = data.GetEnumerator(); while (e.MoveNext()) { int x = 0; x = (int)e.Current; actions.Add(() => x); } foreach (var foo in actions) { Console.WriteLine(foo()); }
這一次,我們將x定義到塊的內部。因此每當循環執行一次,都會產生一個局部變量x,閉包就會對每一個迭代單獨求值,所以輸出就是我們期望的12345了
如果想要得到1,2,3,4,5,可以修改代碼:
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); foreach (int x in data) { int x1 = x; actions.Add(() => x1); } foreach (var foo in actions) { Console.WriteLine(foo()); }
1.7 事件-特殊的委托,例如winform的button的Click事件
自定義事件,泛型事件的調用,事件偵聽器,弱性事件,泛型弱性事件,Expression的用法,內容很多,將在下一章單獨講吧。
留個問題:單個委托綁定2次相同的方法,執行該委托,方法會執行幾次
例如 A+=a;A+=a;A();
======安徽六安=========www.ayjs.net==========aaronyang========楊洋========www.8mi.me==========