[AaronYang]C#人愛學不學[7]


做一個決定,並不難,難的是付諸行動,並且堅持到底 --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==========

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM