程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> CLR筆記:15.委托

CLR筆記:15.委托

編輯:關於.NET

1.Delegate是類型安全的,也就是說,在編譯期可以檢測出錯誤;而與之相似的Reflection是類型不 安全的。

Delegate是方法地址的指針,而且不區分static和instance方法。

Delegate是定義在Class之外的,這個平級的Class中包括Delegate要使用的方法。

2.Delegate允許引用類型的協變(covariance)和反協變(contra-variance)。即:

    delegate object MyCallBack(FileStream fs);
        string SomeOtherMethod(Stream s)

        {
        }

可以將SomeOtherMethod方法綁定到MyCallBack委托上。

"協變"指對於返回類型,方法可以派生於委托。

"反協變"指對於參數類型,委托可以派生於方法。

3.委托的方法回調一般這樣寫:

void CallBack(string p1, string p2, TestDele d)

        {

            if (d != null)

            {

                d(p1, p2);

            }

}

這裡我補充一句廢話:使用CallBack方法是可以傳入null值給TestDele參數的;CallBack方法中要判 斷d值是否為空,null就不能執行方法。

4.Delegate本質:一個類

以下委托聲明:

public delegate string TestDele(int intTest);

等價於這個類:

public class TestDele : System.MulticastDelete

    {
        public TestDele(int intTest, IntPtr method);   
        public virtual string Invoke(int intTest);
        public virtual IAsyncResult BeginResult(int intTest, AsyncCallback callback, 

Object object);
        public virtual void EndInvoke(IAsyncResult result);

 }

委托的繼承關系:如圖

MulticastDelegete中有3個字段非常重要:

字段 類型 描述 _target System.Object 靜態方法時為null;實例方法時為實例對象。對外表現為屬性Target _methodPtr System.IntPtr 一個內部整數值,用來標志回調方法。對外表現為屬性Method,但此時已將整數轉換為 MemberInfo對象 _invocationList System.Object 通常是null,在“委托鏈”中形成一個數組

因為_target和_methodPtr對外表現為屬性Target和Method,所以可以利用這個信息,

1)檢查一個委托是否引用一個特定類型的實例方法:

        bool CheckDeleteTarget(MulticastDelegate d, Type type)

        {

            return ( (d.Target != null) && (d.Target.GetType() == type) 

);

        }

2)檢查回調方法是否有特定的名稱:

        bool CheckDeleteMethod(MulticastDelegate d, string methodName)

        {

            return (d.Method.Name = methodName)

        }

回到3.回調方法中

d(p1, p2);   等價於 d.Invoke(p1, p2),後者是這句話在委托類中的本質。

5.委托鏈回調多個方法

Delegate類提供兩個靜態方法Combine和Remove,

        public static Delegate Combine(Delegate a, Delegate b);

        public static Delegate Remove(Delegate source, Delegate value);

使用如下:

            FeedBack fbChain = null;


            FeedBack fb1 = new FeedBack(TestClass.Function);

            FeedBack fb2 = new FeedBack(TestClass.Function2);


            fbChain = (FeedBack)Delegate.Combine(fbChain, fb1);

            fbChain = (FeedBack)Delegate.Combine(fbChain, fb2);

只有一個委托時,_invocationList指向null;多於一個委托時形成委托鏈,產生一個委托對象數組, _invocationList會指向這個數組。

對於Remove方法,如果移除後委托鏈中不再有對象,則返回null,也就是說,_invocationList中允許 有一個元素的存在(不同於Combine處)

注:對於void型委托,委托鏈會按照隊列順序依次執行方法;對於有返回值的委托,委托鏈返回最後 一個委托的返回值。

在C#中,相應的提供+=與-=來代替Combine和Remove。

6.MulticastDelegate的GetInvocationList()實例方法

如果鏈中一個方法拋出異常,就會停止調用後續對象——這個算法不夠好。

為了避免這個問題,即使遇到異常,拋出後,繼續調用後續的委托方法,可以使用 GetInvocationList():

比如:delegateList為一個委托鏈,那麼通過delegateList.GetInvocationList()就可以得到一個委 托數組arrayDelegates,遍歷這個數組,在其中使用try...catch...捕獲異常,從而所有的委托方法都可 以調用到。

7. 委托中的反射——CreateDelegate()方法與DynamicInvoke()方法

使用委托,是因為在編譯時不知道要使用哪個回調方法,所以說委托是函數指針。

但如果在編譯時,連要使用哪個委托都不知道,或者不知道必須要傳遞哪些參數,這時候就需要反射 的幫助了。

Delegate提供兩個方法:

CreateDelegate(),有若干重載:

    //For 靜態方法委托:

         public static Delegate CreateDelegate(Type type, MethodInfo method);

         public static Delegate CreateDelegate(Type type, MethodInfo method, bool 

throwOnBindFailure);

    //For 實例方法委托:

         public static Delegate CreateDelegate(Type type, object firstArgument, 

MethodInfo method);

         public static Delegate CreateDelegate(Type type, object firstArgument, 

MethodInfo method, bool throwOnBindFailure);

這裡type參數為具體delegate類型,method參數為回調方法名稱,如果有問題就根據 throwOnBindFailure判斷是否拋出異常,不拋出異常時就返回null;對於實例方法委托,還要額外傳入 firstArgument這個實例方法的對象。

可以通過反射獲取type參數和method參數,如下:

Type delType = Type.GetType("delegate類型");

以靜態方法為例:MethodInfo mi = typeof(靜態方法所在類).GetMethod("靜態方法名", BindingFlags.NonPublic | BindingFlags.Static)

DynamicInvoke(params Object[] args),實例方法,傳遞一組在運行時能確定的參數args。

這樣,偽代碼大致如下:

Delegate d;

d.CreateDelegate(delType, mi);

d.DynamicInvoke(params);

8.匿名方法——優雅的委托語法

書上說,使用匿名方法有一個尺度:如果回調方法中多於3行代碼,就不要使用。

我認為,不管幾行,我都不會去用——能看懂別人寫的匿名方法就可以。也許這就是BS程序員與CS程 序員的區別吧。

書中總結了4條,逐條分析:

第1條,直接使用回調函數作為參數,不需要構造委托對象

在下面的示例中,我們看到,SomeAsyncTask()方法作為參數傳遞,而 ThreadPool.QueueUserWorkItem()需要的是一個WaitCallback委托對象參數,.NET可以自動推斷,將方法 解析為委托。以下方法1和方法2其實是一樣的。

    internal sealed class AClass

    {

        public static void CallbackWithoutNewingADelegateObject()

        {

            //方法1

            WaitCallback waitCallback = new WaitCallback(SomeAsyncTask);

            ThreadPool.QueueUserWorkItem(waitCallback, 5);


            //方法2

            ThreadPool.QueueUserWorkItem(SomeAsyncTask, 5);

        }


        private static void SomeAsyncTask(Object o)

        {

            Console.WriteLine(o);

        }

    }

第2條,不需要定義回調方法——"匿名方法"由此而來

public static void CallbackWithoutNewingADelegateObject()
        {

            //方法3,於是可以省略SomeAsyncTask方法

            ThreadPool.QueueUserWorkItem(
                delegate(Object obj) { Console.WriteLine(obj); },

                5);

}

匿名方法中可以有任意行代碼,以上只是一行Console輸出語句

第3條,不需要指定回調方法的參數——不使用方法的參數時,可以省略參數部分

這一條是由第2條演變而來,如下示例:

button1.Click += delegate(Object sender, EventArgs e)
{
MessageBox.Show("Hello!");
};
//簡寫為
button1.Click += delegate
{
MessageBox.Show("Hello!");
};

因為sender和e兩個參數並不使用,所以省略之,但是CLR仍然會在編譯時自動生成這兩個參數。如果 匿名方法中使用了其中任一個參數,還是要全部聲明。

第4條,在回調方法中可以使用外部的變量

還是基於第2條,因為有了匿名方法,從而可以在其中使用外部的一些變量,這樣就不必多建很多參數 傳來傳去的了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved