黑馬程序員-.NET基礎之委託和事件

------- Windows Phone 7手機開發.Net培訓、期待與您交流! -------

 

委託和事件作爲winform,ASP.NET等不論是B/S,還是C/S開發的應用中是司空見慣的存在,我們初學者只有很好的掌握這些理論基礎,才能在後續的學習中靈活且高效的運用這些語言特性。所以現將筆記整理如下。

一、委託

委託是用來處理需用函數指針來處理的情況的
委託是完全面向對象的,是類型安全的
委託是可保存對方法的引用的類。與其他的類不同,委託類具有一個簽名,並且它只能對與其簽名匹配的方法進行引用。這樣,委託就等效於一個類型安全函數指針或一個回調
委託聲明定義一個從 System.Delegate 類派生的類。委託實例封裝了一個調用列表,該列表列出了一個或多個方法,每個方法稱爲一個可調用實體。對於實例方法,可調用實體由該方法和一個相關聯的實例組成。下面給出委託的實例化和調用示例。

using System;
namespace CSharpPractice.Delegate
{
    delegate void D(int[] A);
    class ArraySort
    {
        public static void DisplayMatrix(int[] A)
        {  //打印矩陣
            foreach (int i in A) Console.Write("{0,5} ", i);
            Console.WriteLine();
        }
        public static void GeneralSort(int[] A, D sort)
        {
            sort(A);
            Console.WriteLine("升序數組: ");
            foreach (int i in A) Console.Write("{0,5} ", i);
            Console.WriteLine();
        }
        public static void BubbleSort(int[] A) //冒泡算法
        {
            int i, t;
            int N = A.Length;                  //獲取數組A的長度N
            for (int loop = 1; loop <= N - 1; loop++)//外循環進行N-1輪比較
            {
                for (i = 0; i <= N - 1 - loop; i++)  //內循環兩兩比較,大數下沉
                    if (A[i] > A[i + 1])
                    {
                        t = A[i];
                        A[i] = A[i + 1];
                        A[i + 1] = t;
                    }
            }
        }
        public static void SelectSort(int[] A) //選擇算法
        {
            int i, t, MinI;
            int N = A.Length;                  //獲取數組A的長度N
            for (int loop = 0; loop <= N - 2; loop++) //外循環進行N-1輪比較
            {
                MinI = loop;
                for (i = loop; i <= N - 1; i++)    //內循環中在無序數中找最小值
                    if (A[i] < A[MinI]) MinI = i;
                //最小值與無序數中的第一個元素交換
                t = A[loop];
                A[loop] = A[MinI];
                A[MinI] = t;
            }
        }
        static void Main()
        {
            int[] A = new int[10];
            Random rNum = new Random();
            //數組A賦值(0~100之間的隨機數)
            for (int i = 0; i < A.Length; i++) A[i] = rNum.Next(100);
            Console.WriteLine("原始數組: ");
            DisplayMatrix(A);

            D d1 = new D(ArraySort.BubbleSort);//創建委託實例,指向冒泡算法
            Console.Write("冒泡算法---"); 
            GeneralSort(A, d1);
            D d2 = new D(ArraySort.SelectSort);//創建委託實例,指向選擇算法
            Console.Write("選擇算法---");
            GeneralSort(A, d2);
            Console.ReadKey();
        }
    }
}


 

2.匿名方法委託

無需先聲明類或結構以及與委託匹配的方法,而是在創建委託的實例時,直接聲明與委託匹配的方法的代碼塊(匿名方法),匿名方法委託例示。

using System;
namespace CSharpPractice.Delegate
{
    // 聲明委託
    delegate void Printer(string s);
    class TestClass
    {
        // 與命名委託相關的方法:
        static void DoWork(string k)
        {
            Console.WriteLine(k);
        }
        static void Main()
        {
            // 使用匿名方法實例化delegate類:
            Printer p = delegate(string j)
            {
                Console.WriteLine(j);
            };
            // 匿名delegate調用結果:
            p("使用匿名方法的委託的調用.");
            // 使用"DoWork"方法對delegate實例化:
            p = new Printer(TestClass.DoWork);
            // 傳統delegate調用結果:
            p("使用命名方法的委託的調用.");
            Console.ReadLine();
        }
    }
}

 

3.多播委託

委託也可以包含多個方法,這種委託稱爲多播委託
如果調用多播委託實例,則按順序依次調用多播委託實例封裝的調用列表中的多個方法
聲明多播委託時,其返回類型必須爲void,因爲無法處理多次調用的返回值,而且不能帶輸出參數(但可以帶引用參數)
多播委託通過 + 或 += 向多播委託實例封裝的調用列表中添加方法;通過 – 或 -= 從多播委託實例封裝的調用列表中刪除方法。

using System;
namespace CSharpPractice.Delegate
{
    delegate void D(int x);
    class C
    {
        public static void M1(int i)
        {
            Console.WriteLine("C.M1: " + i);
        }
        public static void M2(int i)
        {
            Console.WriteLine("C.M2: " + i);
        }
        public void M3(int i)
        {
            Console.WriteLine("C.M3: " + i);
        }
    }
    class Test
    {
        static void Main()
        {
            D cd1 = new D(C.M1);
            cd1(-1); // call M1 
            D cd2 = new D(C.M2);
            cd2(-2); // call M2 
            D cd3 = cd1 + cd2;
            cd3(10); // call M1 then M2 
            cd3 += cd1; cd3(20); // call M1, M2, then M1 
            C c = new C();
            D cd4 = new D(c.M3);
            cd3 += cd4; cd3(30); // call M1, M2, M1, then M3
            cd3 -= cd1; // remove last M1 
            cd3(40); // call M1, M2, then M3 
            cd3 -= cd4; cd3(50); // call M1 then M2 
            cd3 -= cd2; cd3(60); // call M1 
            cd3 -= cd2; // impossible removal is benign 
            cd3(60); // call M1 
            cd3 -= cd1; // invocation list is empty so cd3 is null 
            // cd3(70); // System.NullReferenceException thrown 
            cd3 -= cd1; // impossible removal is benign 
            Console.ReadLine();
        }
    }
}

 

4.委託的兼容性

D和M的參數數目相同,且各自對應參數具有相同的ref或out修飾符;(D指委託,M指方法)
對於每個ref或out參數,D中的參數類型與M中的參數類型相同。
存在從M的返回類型到D的返回類型的標識或隱式引用轉換。即允許方法具有的派生返回類型比委託中定義的更多(協變)。
每一個值參數(沒有 ref 或 out 修飾符的參數)都存在從D中的參數類型到M中的對應參數類型的標識或隱式引用轉換。允許方法具有的派生參數類型比委託類型中的更少(逆變)

using System;
namespace CSharpPractice.Delegate
{
    class Mammals
    {
        //...
    }
    class Dogs : Mammals
    {
        //...
    }
    class Program
    {  // 定義委託
        public delegate Mammals HandlerMethod();
        public delegate void HandlerMethod1(Mammals m);
        public delegate void HandlerMethod2(Dogs d);
        public static Mammals FirstHandler()
        {
            Console.WriteLine("first handler");
            return null;
        }
        public static Dogs SecondHandler()
        {
            Console.WriteLine("second handler");
            return null;
        }
        public static void ThirdHandler(Mammals m)
        {
            Console.WriteLine("third handler");
        }
        static void Main()
        {
            HandlerMethod handler1 = FirstHandler;
            handler1();
            //協變
            HandlerMethod handler2 = SecondHandler;
            handler2();
            Mammals m = new Mammals();
            HandlerMethod1 handler11 = ThirdHandler;
            handler11(m);
            //逆變
            Dogs d = new Dogs();
            HandlerMethod2 handler22 = ThirdHandler;
            handler22(d);
            Console.ReadKey();
        }
    }
}


 

 

二、事件

類或對象可以通過事件(event)向其他類或對象通知發生的相關事情。發送(或引發)事件的類稱爲“發行者”(生產者),接收(或處理)事件的類稱爲“訂戶”(消費者)。
事件是一種使對象或類能夠提供通知的成員。客戶端可以通過提供事件處理程序(event handler)爲相應的事件添加可執行代碼。
事件是對象發送的消息,以發信號通知操作的發生。操作可能是由用戶交互(例如鼠標單擊)引起的,也可能是由某些其他的程序邏輯觸發的。

 

2.事件特點

發行者確定何時引發事件,訂戶確定執行何種操作來響應該事件
一個事件可以有多個訂戶。一個訂戶可處理來自多個發行者的多個事件
沒有訂戶的事件永遠不會被調用
事件通常用於通知用戶操作,例如,圖形用戶界面中的按鈕單擊或菜單選擇操作
如果一個事件有多個訂戶,當引發該事件時,會同步調用多個事件處理程序
可以利用事件同步線程
在 .NET Framework 類庫中,事件是基於 EventHandler 委託和 EventArgs 基類的

3.事件處理機制

事件實際上是委託的一種特殊形式。C# 使用一種委託模型來實現事件。事件模型分爲事件生產者和事件消費者,其處理機制大致可以分爲下列4步
在事件生產者類中聲明一個事件成員,即某種事件處理委託(簡稱爲事件委託)的實例(多播事件委託實例);
在事件消費者類中聲明與事件委託相匹配的事件處理方法;
通過“+=”向多播事件委託實例封裝的調用列表中添加事件處理方法,或通過“-=”從多播事件委託實例封裝的調用列表中刪除事件處理方法;
在事件生產者類中添加有關發生事件的代碼,即當滿足某種條件時(發生事件),則調用委託,即調用多播事件委託實例封裝的調用列表中添加的事件處理方法。如果沒有訂閱,即事件實例爲Null,則不作任何處理。

下面給出實現事件步驟的代碼。算是對初步掌握事件一個總結吧。

<span style="font-size:18px;">using System;
using System.Collections;
namespace CSharpPractice.Event
{
    //步驟1:聲明提供事件數據的類。
    public class NameListEventArgs : EventArgs
    {
        public string Name { get; set; }
        public int Count { get; set; }

        public NameListEventArgs(string name, int count)
        {
            Name = name;
            Count = count;
        }
    }
    //步驟2:聲明事件處理委託。
    public delegate void NameListEventHandler(object source, NameListEventArgs args);
    //步驟3:聲明引發事件的類(事件生產類)。
    public class NameList
    {
        ArrayList list;
        //步驟4:在事件生產類中,聲明事件。
        public event NameListEventHandler nameListEvent;
        public NameList()
        {
            list = new ArrayList();
        }
        public void Add(string Name)
        {
            list.Add(Name);
            //步驟5:在事件生產類中,實現產生事件的代碼。
            if (nameListEvent != null)
            {
                nameListEvent(this, new NameListEventArgs(Name, list.Count));
            }
        }
    }
    //步驟6:聲明處理事件的類(事件消費類)。
    public class EventDemo
    {
        //步驟7:在事件消費類中,聲明事件處理方法。
        public static void Method1(object source, NameListEventArgs args)
        {
            Console.WriteLine("列表中增加了項目:{0}", args.Name);
        }
        //步驟7:在事件消費類中,聲明事件處理方法。
        public static void Method2(object source, NameListEventArgs args)
        {
            Console.WriteLine("列表中的項目數:{0}", args.Count);
        }
        public static void Main()
        {
            NameList nl = new NameList();
            //步驟8:在事件消費類中,訂閱或取消事件。
            nl.nameListEvent += new NameListEventHandler(EventDemo.Method1);
            nl.nameListEvent += new NameListEventHandler(EventDemo.Method2);
            nl.Add("張三");
            nl.Add("李四");
            nl.Add("王五");
            Console.ReadLine();
        }
    }
}</span>

寫了這麼久,好累啊,該出去走走了,放鬆放鬆。。

 

------- Windows Phone 7手機開發.Net培訓、期待與您交流! -------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章