C#中的委托解析

来源:http://www.cnblogs.com/pengze0902/archive/2016/11/22/6088870.html
-Advertisement-
Play Games

談及到C#的基本特性,“委托”是不得不去瞭解和深入分析的一個特性。對於大多數剛入門的程式員談到“委托”時,都會想到“將方法作為方法的參數進行傳遞”,很多時候都只是知道簡單的定義,主要是因為“委托”在理解上有較其他特性比較難的地方。在本次說明中,不會將委托的簡單聲明和調用作為重點。 “委托”不需要直接 ...


    談及到C#的基本特性,“委托”是不得不去瞭解和深入分析的一個特性。對於大多數剛入門的程式員談到“委托”時,都會想到“將方法作為方法的參數進行傳遞”,很多時候都只是知道簡單的定義,主要是因為“委托”在理解上有較其他特性比較難的地方。在本次說明中,不會將委托的簡單聲明和調用作為重點。

    “委托”不需要直接定義一個要執行的行為,而是將這個行為用某種方法“包含”在一個對象中。這個對象可以像其他任何對象那樣使用。在該對象中,可以執行封裝的操作。可以選擇將委托看作之定義了一個方法的介面,將委托的實例看作實現了那個介面的對象。

    在“委托”的相關定義中,我們可以不難看出,“委托與方法“相比較於“介面與類”有著設計理念上的相似部分,產生的背景源於”設計原則“中的”開放-封閉原則“,”開放-封閉“原則:是說軟體實體(類,模塊,函數等等)應該可以擴展,但是不可修改。換一種說法可能更好的理解”對於擴展是開放的,對於更改是封閉的“,面對新的需求,對於程式的改動是通過增加新的代碼進行的,而不是更改現有的代碼。

   在C#中委托用delegate關鍵字定義,使用new操作符構造委托實例,採用傳統的方法調用語法來回調函數(只是要用引用了委托對象的一個變數代替方法名)。在C#中,委托在編譯的時候會被編譯成類。對於委托的一個說明:委托是一個類,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞。委托類既可嵌套在一個類型中定義,也可以在全局範圍內定義。由於委托是類,凡是可以定義類的地方,都可以定義委托。

  接下來我們來看一下”委托“的組成,需要滿足的條件:

     1.聲明委托類型。

     2.必須有一個方法包含了要執行的代碼。

     3.必須創建一個委托實例。

     4.必須調用委托實例。

    接下來大致的瞭解一下上面所提出的4項條件:

     委托類型實際上只是參數類型的一個列表以及返回類型。規定了類型的實例能表示的操作。在調用一個委托實例的時候,必須保證使用的參數完全匹配,而且能以指定的方式使用返回值。對於委托實例的創建,取決於操作使用實例方法還是靜態方法(如果操作是靜態方法,指定類型名稱就可以,如果是操作實例方法,需要先創建類型的實例)。對於委托的調用,可以直接調用委托的實例的方法就可以完成對應的操作。

    以上談及了”委托“的定義和組成,接下來我們來瞭解一下如何將方法綁定到”委托“上,以及委托的合併和刪除。

    可以將多個方法賦給同一個委托,委托實例實際有一個操作列表與之關聯。在System.Delegate類型中提供了兩個靜態方法Combine()和Remove()負責委托實例的新增和刪除操作。但是在我們的實際開發中,較多的採用-=和+=操作符。

  在FCL中,所有的委托類型都派生自MulticastDelegate,該類型在System.MulticastDelegate類型中。

   具體來看一下Combine()方法的底層實現代碼:

 [System.Runtime.InteropServices.ComVisible(true)] 
        public static Delegate Combine(params Delegate[] delegates) 
        {
            if (delegates == null || delegates.Length == 0) 
                return null;

            Delegate d = delegates[0];
            for (int i = 1; i < delegates.Length; i++) 
                d = Combine(d,delegates[i]);
 
            return d; 
        }
public static Delegate Combine(Delegate a, Delegate b) 
        {
            if ((Object)a == null) 
                return b;

            return  a.CombineImpl(b);
        } 

    以上兩個方法為System.Delegate類型中,CombineImpl方法在MulticastDelegate重寫。

        [System.Security.SecuritySafeCritical]  
        protected override sealed Delegate CombineImpl(Delegate follow)
        { 
            if ((Object)follow == null) 
                return this;
            if (!InternalEqualTypes(this, follow))
                throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));
 
            MulticastDelegate dFollow = (MulticastDelegate)follow;
            Object[] resultList; 
            int followCount = 1; 
            Object[] followList = dFollow._invocationList as Object[];
            if (followList != null) 
                followCount = (int)dFollow._invocationCount;

            int resultCount;
            Object[] invocationList = _invocationList as Object[]; 
            if (invocationList == null)
            { 
                resultCount = 1 + followCount; 
                resultList = new Object[resultCount];
                resultList[0] = this; 
                if (followList == null)
                {
                    resultList[1] = dFollow;
                } 
                else
                { 
                    for (int i = 0; i < followCount; i++) 
                        resultList[1 + i] = followList[i];
                } 
                return NewMulticastDelegate(resultList, resultCount);
            }
            else
            { 
                int invocationCount = (int)_invocationCount;
                resultCount = invocationCount + followCount; 
                resultList = null; 
                if (resultCount <= invocationList.Length)
                { 
                    resultList = invocationList;
                    if (followList == null)
                    {
                        if (!TrySetSlot(resultList, invocationCount, dFollow)) 
                            resultList = null;
                    } 
                    else 
                    {
                        for (int i = 0; i < followCount; i++) 
                        {
                            if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
                            {
                                resultList = null; 
                                break;
                            } 
                        } 
                    }
                } 
                if (resultList == null)
                {
                    int allocCount = invocationList.Length; 
                    while (allocCount < resultCount)
                        allocCount *= 2; 
 
                    resultList = new Object[allocCount];
 
                    for (int i = 0; i < invocationCount; i++)
                        resultList[i] = invocationList[i];

                    if (followList == null) 
                    {
                        resultList[invocationCount] = dFollow; 
                    } 
                    else
                    { 
                        for (int i = 0; i < followCount; i++)
                            resultList[invocationCount + i] = followList[i];
                    }
                } 
                return NewMulticastDelegate(resultList, resultCount, true);
            } 
        } 

   再來具體看一下Remove()方法的底層實現代碼,RemoveAll和Remove兩個方法為System.Delegate類型中,CombineImpl方法在MulticastDelegate重寫。:

 public static Delegate RemoveAll(Delegate source, Delegate value) 
        {
            Delegate newDelegate = null; 

            do
            {
                newDelegate = source; 
                source = Remove(source, value);
            } 
            while (newDelegate != source); 

            return newDelegate; 
        }
[System.Security.SecuritySafeCritical] 
        public static Delegate Remove(Delegate source, Delegate value)
        {
            if (source == null) 
                return null;
 
            if (value == null) 
                return source;
 
            if (!InternalEqualTypes(source, value))
                throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));

            return source.RemoveImpl(value); 
        }

        [System.Security.SecuritySafeCritical] 
        protected override sealed Delegate RemoveImpl(Delegate value)
        {             MulticastDelegate v = value as MulticastDelegate; 

            if (v == null) 
                return this; 
            if (v._invocationList as Object[] == null)
            { 
                Object[] invocationList = _invocationList as Object[];
                if (invocationList == null)
                {
                    if (this.Equals(value))
                        return null; 
                } 
                else
                { 
                    int invocationCount = (int)_invocationCount;
                    for (int i = invocationCount; --i >= 0; )
                    {
                        if (value.Equals(invocationList[i])) 
                        {
                            if (invocationCount == 2) 
                            { 
                                return (Delegate)invocationList[1-i]; 
                            }
                            else
                            {
                                Object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); 
                                return NewMulticastDelegate(list, invocationCount-1, true);
                            } 
                        } 
                    }
                } 
            }
            else
            {
                Object[] invocationList = _invocationList as Object[]; 
                if (invocationList != null) {
                    int invocationCount = (int)_invocationCount; 
                    int vInvocationCount = (int)v._invocationCount; 
                    for (int i = invocationCount - vInvocationCount; i >= 0; i--)
                    { 
                        if (EqualInvocationLists(invocationList, v._invocationList as Object[], i, vInvocationCount))
                        {
                            if (invocationCount - vInvocationCount == 0)
                            { 
                                return null; 
                            } 
                            else if (invocationCount - vInvocationCount == 1)
                            { 
                                return (Delegate)invocationList[i != 0 ? 0 : invocationCount-1];
                            }
                            else 
                            {
                                Object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); 
                                return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); 
                            }
                        } 
                    }
                }
            }
 
            return this;
        } 

  在以上的代碼中,我們瞭解到了在.NET底層是如何實現委托實例的綁定和刪除綁定。

  在調用委托實例時,所有的操作都是順序執行的。如果調用具有一個非void的返回類型,則調用的返回值是最後一個操作的返回值。如果調用列表中任何操作拋出異常,都會阻止執行後續的操作。

   在上面提到了委托列表中出現非void實例調用,如果委托實例中出現多個非void調用,並且需要獲取所有的委托實例的返回值結果,那麼應該如何操作,在.NET紅提供了一個方法GetInvocationList(),用於獲取委托鏈表。

  接下來具體瞭解一下GetInvocationList()的底層代碼:

      [System.Security.SecuritySafeCritical] 
        public override sealed Delegate[] GetInvocationList()
        {
            Delegate[] del;
            Object[] invocationList = _invocationList as Object[];
            if (invocationList == null)
            { 
                del = new Delegate[1];
                del[0] = this; 
            } 
            else
            { 
                int invocationCount = (int)_invocationCount;
                del = new Delegate[invocationCount]; 

                for (int i = 0; i < invocationCount; i++) 
                    del[i] = (Delegate)invocationList[i]; 
            }
            return del; 
        }

   當獲取到委托實例列表後,可採用迴圈迭代的方式,依次獲取每個委托實例的返回值。

   再來瞭解一個屬性Method,具體看一下此屬性的底層實現代碼:

       public MethodInfo Method 
        {
            get
            {
                return GetMethodImpl(); 
            }
        } 
 
        [System.Security.SecuritySafeCritical] 
        protected virtual MethodInfo GetMethodImpl() 
        {
            if ((_methodBase == null) || !(_methodBase is MethodInfo))
            {
                IRuntimeMethodInfo method = FindMethodHandle(); 
                RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method);
                if (RuntimeTypeHandle.IsGenericTypeDefinition(declaringType) || RuntimeTypeHandle.HasInstantiation(declaringType)) 
                {
                    bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; 
                    if (!isStatic)
                    {
                        if (_methodPtrAux == (IntPtr)0)
                        { 
                            Type currentType = _target.GetType(); 
                            Type targetType = declaringType.GetGenericTypeDefinition(); 
                            while (currentType != null)
                            { 
                                if (currentType.IsGenericType &&
                                    currentType.GetGenericTypeDefinition() == targetType)
                                {
                                    declaringType = currentType as RuntimeType; 
                                    break;
                                } 
                                currentType = currentType.BaseType; 
                            }

                            BCLDebug.Assert(currentType != null || _target.GetType().IsCOMObject, "The class hierarchy should declare the method"); 
                        }
                        else 
                        { 
                            MethodInfo invoke = this.GetType().GetMethod("Invoke"); 
                            declaringType = (RuntimeType)invoke.GetParameters()[0].ParameterType;
                        }
                    }
                } 
                _methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method);
            } 
            return (MethodInfo)_methodBase; 
        }

    以上是System.Delegate類中的定義,接下來看一下MulticastDelegate重寫:

 [System.Security.SecuritySafeCritical] 
        protected override MethodInfo GetMethodImpl()
        { 
            if (_invocationCount != (IntPtr)0 && _invocationList != null) 
            {
                Object[] invocationList = _invocationList as Object[];
                if (invocationList != null)
                {
                    int index = (int)_invocationCount - 1; 
                    return ((Delegate)invocationList[index]).Method;
                } 
                MulticastDelegate innerDelegate = _invocationList as MulticastDelegate; 
                if (innerDelegate != null)
                { 
                    return innerDelegate.GetMethodImpl();
                }
            } 
            else if (IsUnmanagedFunctionPtr())
            { 
                if ((_methodBase == null) || !(_methodBase is MethodInfo)) 
                {
                    IRuntimeMethodInfo method = FindMethodHandle();
                    RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method);
                    if (RuntimeTypeHandle.IsGenericTypeDefinition(declaringType) || RuntimeTypeHandle.HasInstantiation(declaringType)) 
                    { 
                        RuntimeType reflectedType = GetType() as RuntimeType; 
                        declaringType = reflectedType;
                    }
                    _methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method);
                } 
                return (MethodInfo)_methodBase;
            } 
            return base.GetMethodImpl(); 
        }

   以上是對委托的相關定義,以及有關委托的一些操作方法的說明,沒有具體指出如何去創建和使用委托,因為委托的簡單創建和一般應用,對於大部分開發者來說是相對較為簡單的,因為微軟在不斷的對C#的語法進行提升和修改,極大的簡化了對應的操作。但是正是由於在應用層做了較大的封裝,這也會導致特性在底層的複雜度慢慢的增大。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • MVVM下RichTextBox的鍵盤迴車事件設置為發送,不是回車 ...
  • 我們經常會碰到生成Excel 界面併在其上操作的功能開發。 比如如下界面,我們需要在菜單里添加一個菜單按鈕“Columns To Rows Transform" 功能是對多列批量轉成多行. 我們可以通過Interop組件,在其中添加菜單附加功能,這個在之後的隨筆中我會寫個Demo,供大家查看。但是這 ...
  • 百度編輯器UEditor與UEditor 公式插件完整Demo ...
  • 可用於對比文件是否相同 ...
  • .NET框架下最全面的報表解決方案,支持多種報表導出格式,擁有簡單且強大的報表引擎。本系列教程適合Stimulsoft Reports上手入門。 ...
  • 上個項目中用到了上傳視頻,本來打算用百度的webuploader做的,但是webuploader可能有個毛病就是不能上傳太大的東西。 於是乎,只能換個方法做了啊,看了半天最終決定用傳統的uploaderfile來做。順便吐槽下uploaderfile真的是,和webuploader相比完全是有點複雜 ...
  • 進程:運行應用程式實例 線程:對CPU進行虛擬化。windows為每個進程都提供了該進程專用的線程,這樣當一個進程“假死”,不會影響到其他進程。 線程開銷 線程可以使windows在長時間運行任務可以隨時得到響應,允許用戶使用一個應用程式將已凍結的應用程式強制結束。 線程內核對象(thread ke ...
  • 效果圖: style: xaml.CS: Loaded: emoticons.xml: <?xml version="1.0"?><Emoticons> <Emoticon id="0" tip="微笑">0.gif</Emoticon> <Emoticon id="1" tip="撇嘴">1.gi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...