.NET漫遊指南-002-委託

什麼是委託?爲什麼要用委託?委託有那幾種形式?怎麼使用委託?

委託的定義:

委託是一種特殊類型的對象(也就是說,可以看作是一個特殊的類),包含一個或者多個方法的地址。

爲什麼要用委託:

當需要把一個方法進行傳遞時就需要用到委託來實現,在C/C++的時候是提取函數地址的指針進行傳遞的,但是這樣是沒有安全性的,因爲你無法對其進行安全性校驗,這就造成了編碼的不可控性,非法的數據就可能被調用。在.NET Framework中就加入了委託的概念,如果要傳遞方法,就必須把方法的細節封裝在一個新型的對象中–委託。所以委託是針對方法進行的操作,委託對數據是什麼並不關心(也是面向對象編程思路,只關心自己要處理的)。

委託有幾種形式:

1,自定義委託
2,泛型委託:Action,Func,自定義泛型委託
3,匿名委託
4,多播委託
5,Lambda形式的委託

從返回形式來看,委託可以分爲兩種。一種是有返回值的委託,一種是沒有返回值的委託(可以成爲事件委託)。這兩種形式的委託定義,聲明和委託綁定還是有區別的。如下做簡要展示。

//定義有返回值的委託
pubilc delegate string GenericDelegate<T,S>(T title, S author);
//定義無返回值的事件委託
public delegate void GenericDelegateEvent<E,P>(E Name, P Address);

public class GenericDelegateClass<V,F>
{
   //聲明委託
   GenericDelegate<V,F> GDeleValue;
   //聲明事件委託
   GenericDelegateEvent<V,F> GDeleEvent = null;
}

//綁定委託
pubilc void Click(object sender , EventArgs e)
{
  GenericDelegateClass<string ,string> gdc = new GenericDelegateClass<string ,string>();
  //綁定委託,可以用=。
  gdc.GDeleValue = new GenericDelegate<string,string>(Delegate);
  //綁定事件委託,事件委託只能用+=,-=。
  gdc.GDeleEvent += new GenericDelegateEvent<stirng,string>(EventDelegate);

    public string Delegate<T,S>(T title,S author)
    {
      return title.ToString() + author;
    }

    private void EventDelegate<V, F>(V name, F address)
    {
      //
    }
}

下面舉個例子來幫助理解委託的工作方式,這也是面向對象編程的思維方式。
故事裏面有三個要素:任務下達,你的組長,你。
今天公司給你們組下達了一個開發任務,你組長接受到任務之後覺得你適合完成這個任務,就將任務委託給了你去實現,你做完之後把結果給你組長。
其實委託的實現流程和上面是一樣的(所以叫委託= =)

怎麼使用委託

下面用事件來闡述如何使用委託
委託的定義語法如下:
delegate void IntMethodInvoker(int x);
關鍵詞delegate 表示這是個委託對象,void和int參數表示該委託包含的方法帶有一個int型的參數且返回值是void。這也體現了委託的安全性。(爲什麼委託的返回類型要和調用的方法的返回類型一樣,一方面是爲了保證委託的安全性,另一方面方法的處理結果要靠委託表達出來,所以要一樣)
委託的實例化
委託的實例化和普通類的實例化是一樣
IntMethodInvoker invoker = new IntMethodInvoker( );
代碼下載地址:http://download.csdn.net/download/geshicuowu/9933392
下面舉個最簡單的例子來展現委託
委託
很明顯結果會返回:40
但是上面的這個例子顯然沒有說到點上,委託的使用看起來和直接使用x.ToString( )方法沒有什麼特別的優勢。(在後面會舉一個更能說明委託作用的一個例子。該例子主要是爲了表明委託的基本形式,以後的形式都是在此上面的擴展)

泛型委託

其實在實際的項目中,泛型委託的形式更爲常見。其中action委託表示引用一個返回void類型的方法,參數範圍是0~16個。func委託可以引用具有返回類型的方法。例如func(in T1,out TResult)
下面將舉一個泛型委託的例子,也更能體現出委託的特點。(例子來源於c#高級編程)
1:定義一個排序類,裏面聲明一個static方法,其中一個參數爲泛型委託func

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 這個類時用來演示泛型委託的
/// 冒泡排序
/// </summary>
namespace DelegateExample
{
    class BubbleSort
    {
        /// <summary>
        /// 冒泡排序方法
        /// </summary>
        /// <typeparam name="T">泛型參數</typeparam>
        /// <param name="sortArry">傳入的排序隊列</param>
        /// <param name="comparison">泛型委託</param>
        /// <returns></returns>
        static public bool Sort<T>(IList<T> sortArry, Func<T, T, bool> comparison)
        {
            for (int j = 0; j < sortArry.Count; j++)
            {
                for (int i = j+1; i < sortArry.Count; i++)
                {
                    if (comparison(sortArry[j], sortArry[i]))
                    {
                        var temp = sortArry[j];
                        sortArry[j] = sortArry[i];
                        sortArry[i] = temp;
                    }
                }
            }
            return false;
        }
    }
}

2:定義一個employee類,其中有工資的比較方法,和上面的泛型委託的參數和返回類型相匹配。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 自定義的一個員工類
/// 用來示範泛型委託的使用
/// author:zhangzhen
/// date:   2017/08/14
/// </summary>
namespace DelegateExample
{
    class Employee
    {
        public Employee(string name, double salery)
        {
            Name = name;
            Salery = salery;
        }

        public string Name
        {
            get;
            private set;
        }

        public double Salery
        {
            get;
            private set;
        }
        /// <summary>
        /// 比較薪資的函數
        /// </summary>
        /// <param name="e1"></param>
        /// <param name="e2"></param>
        /// <returns></returns>
         static public bool  compare(Employee e1,Employee e2)
        {
            return e1.Salery < e2.Salery;
        }
    }
}

3:最後時實現類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*******************************************
 * 這個控制檯程序主要用來演示委託delegate的相關知識
 * author:zhangzhen
 * date   :2017-08-10   
 * ******************************************/
namespace DelegateExample
{
    class Program
    {    //定義一個委託
        delegate string GetStringInvoker();
        static void Main(string[] args)
        {
            Employee[] employees =
            {
                new Employee("zhangzhen",10000000),
                new Employee("xiaoming",10000),
                new Employee("xiaohuang",20000),
                new Employee("xiaodong",8000)
            };
            BubbleSort.Sort(employees, Employee.compare);
            foreach(var item in employees)
            {
                Console.WriteLine(item.Name + " /// "+ item.Salery);
            }
            //讓控制檯暫停等待輸入。
            Console.ReadKey();
        }
    }
}

輸出結果符合預期。
也就是說任何對象(只要該對象中有比較方法,且比較方法的形式符合BubbleSort類中Sort方法中的泛型委託的要求的)都可以使用Sort方法實現冒泡排序。試想,如果沒有委託(尤其是泛型委託)的話,這個效果時很難實現的。

多播委託

上面那個排序的例子中,調用一次委託就調用一次方法,如果要調用多個方法,就要多次顯示的調用這個委託,使用起來不是很方便,多播委託就比較好的解決了這個問題。
多播委託可以委託多個方法,在調用多播委託時,將按順序連續調用多個方法。(因爲不可能返回多個返回類型,因此多播委託的簽名必須是void)多播委託可以使用-=,+=來向委託列表中刪減方法或者增加方法。
使用多播委託時的注意點:
1:多播委託並不會嚴格的按照委託列表中的順序來執行,也就是說不能把有順序特定依賴的方法加到委託列表中。
2:多播委託中,如果委託鏈表中一個方法發生了異常,整個迭代就會終止。爲了避免這個問題,Delegate類中定義了GetInvocationList()方法,它返回一個Delegate對象數組,可以利用這個委託調用與委託列表中相關的方法,同時捕獲異常,並不會終止迭代。
在一些涉及業務較爲繁雜的項目中,泛型委託和多播委託一般都會結合使用。
下面就是一個比較簡單的例子(因爲多播委託中委託的方法返回類型是void,所以邏輯處理基本都是在被委託的方法中實現)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 這個類是用來演示多播委託的一個輔助類
/// </summary>
namespace DelegateExample
{
    class MultiDelegate
    {
        static public void One()
        {
            Console.WriteLine("這是方法ONE");
        }
        static public void Two()
        {
            Console.WriteLine("這是方法Two");
        }
    }
}

下面是實現

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*******************************************
 * 這個控制檯程序主要用來演示委託delegate的相關知識
 * author:zhangzhen
 * date   :2017-08-10   
 * ******************************************/
namespace DelegateExample
{
    class Program
    {
            /*********************多播委託的演示**********************************/
            Action action = MultiDelegate.One;
            action += MultiDelegate.Two;
            action += MultiDelegate.One;
            //將Two方法從委託鏈表中移除
            //action -= MultiDelegate.Two;

            Delegate[] tempDelegate = action.GetInvocationList();
            //下面的形式是不可行的,var沒辦法調用委託中的方法,必須用Action來修飾item
            //foreach (var item in tempDelegate)
            //{
            //    item();
            //}
            foreach (Action item in tempDelegate)
            {
                item();
            }
            //讓控制檯暫停等待輸入。
            Console.ReadKey();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章