1. 程式人生 > 實用技巧 >委託的常見用法

委託的常見用法

  此篇文章是我一個小白對委託的理解和總結,請高手多多評判指教。

  委託就是一種後期繫結機制,說的直白點就是在呼叫的時候才去傳遞業務邏輯的一種演算法。

委託的建立語法:

public delegate int Comparison<in T>(T left, T right);//官方給出的定義泛型委託的demo

  語法看似像宣告一個變數或方法的簽名,但實現上是在宣告一個型別。編譯器會生成一個派生自System.MulticastDelegate的類(而System.MulticastDelegate派生自System.Delegate),型別名與委託的名字相同,其中包含Invoke 、BeginInvoke和EndInvoke等方法。編譯器還為這個新型別生成新增和刪除處理業務,以便該類的客戶端可以在例項的呼叫列表中新增和刪除方法。

  委託可以被定義在類的內部、名稱空間下(與類同級)和全域性名稱空間下(不推薦)。

//在全域性定義
public delegate int Comparison<in T>(T left, T right);
namespace Test
{
    //在指定名稱空間下定義
    public delegate int Comparison2<in T>(T left, T right);
    public class Student
    {
        //在類內部定義
        public delegate int Comparison3<in
T>(T left, T right); } }

委託的定義賦值

  將委託當成類使用(委託本身就是一個類)。

//定義委託
public delegate int Comparison<in T>(T left, T right);
public class Test
{
    //定義
    private Comparison<int> comparator;
    public void Show()
    {
        //賦值 請注意,使用的是方法名稱,不帶括號,將方法附加給委託作為委託的呼叫方法。
        this.comparator = Compare;
        
//呼叫 this.comparator(1, 2);
     //呼叫方式二
this.comparator.Invoke(1, 2); }
private int Compare(int left, int right) => left.CompareTo(right); }

當用作委託的目標方法是“小方法”的情況下,通常使用lambda表示式語法來執行賦值:

Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);

多播委託

  通常只是將單個目標方法附加到委託。但是,委託物件確實支援將多個目標方法附加到一個委託物件的呼叫列表,稱為多播委託。多播委託意味著通過委託呼叫時可以呼叫多個方法,所以可以為委託附加多個方法。

private int Cal(int num) {return num * num;}
private void button1_Click(object sender, EventArgs e)
{
     Func<int, int> action = null;
     //使用+= 或 -= 來增加或移除委託例項
     action += a => { Console.WriteLine($"第{1}次執行" + a); return a + 1; };
     action += Cal;
     action += a => { Console.WriteLine($"第{3}次執行" + a); return a + 3; };
     action -= Cal;
     //如果有返回值的話,返回的是最後一次執行的結果
     int a = action(5);
     Console.WriteLine(a);
}

常用泛型委託

無返回值

public delegate void Action();
public delegate void Action<in T>(T arg);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
// Other variations removed for brevity.

有返回值

public delegate TResult Func<out TResult>();
public delegate TResult Func<in T1, out TResult>(T1 arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
// Other variations removed for brevity

返回bool型別

public delegate bool Predicate<in T>(T obj);

注意:在.net core平臺中,不支援委託目標方法的非同步呼叫(即不支援BeginInvoke和EndInvoke方法),在Framework中的用法如下:

 Action action = () => Console.WriteLine("委託執行了");
 AsyncCallback asyncCallback = a => Console.WriteLine("回撥執行了" + a);
 IAsyncResult result = action.BeginInvoke(asyncCallback, "asdf"); //委託啟動非同步呼叫
 action.EndInvoke(result);

但是在 .NET Core平臺下會丟擲如下異常:

 //System.PlatformNotSupportedException:“Operation is not supported on this platform.”

這是因為我自定義的委託型別上並沒有BeginInvoke和EndInvoke方法(MulticastDelegate和Delegate類中也沒有定義),那BeginInvoke和EndInvoke方法究竟是怎麼來的呢?通過反射可以得知這些方法是由編譯器生成的

Event 事件

事件是帶event關鍵字的委託的例項,event可以限制被外部呼叫(invoke)和直接賦值(=)。委託是一個型別,而事件是委託型別的一個例項

  宣告一個事件很簡單,只需在宣告一個委託物件時加上event關鍵字就行。

/// <summary>
/// 定義一個委託
/// </summary>
/// <param name="name"></param>
public delegate void ShowInfo(string name);
public class Study
{
    /// <summary>
    /// 宣告一個事件
    /// </summary>
    public event ShowInfo ShowInfo;
    public void Show()
    {
        ShowInfo += Study_ShowInfo;
        //只能在“publisher”類中呼叫
        ShowInfo("asdf");
    }
    private void Study_ShowInfo(string name)
    {
        throw new NotImplementedException();
    }
}

可以在用事件的地方用委託來替代,但事件有一系列規則和約束用以保證程式的安全可控,事件只有 += 和 -= 操作,這樣訂閱者只能有訂閱或取消訂閱操作,沒有許可權執行其它操作。如果是委託,那麼訂閱者就可以使用 = 來對委託物件重新賦值(其它訂閱者全部被取消訂閱),甚至將其設定為null,甚至訂閱者還可以直接呼叫委託,這些都是很危險的操作,廣播者就失去了獨享控制權。

事件保證了程式的安全性和健壯性。