C#-委托

委托

  • 如果我們要把方法當(dāng)做參數(shù)來傳遞,就用到委托,簡單來說,委托是一個類型,這個類型可以賦值一個方法的引用。

聲明委托

  • 在c#中使用一個類分兩個階段,首先定義這個類,告訴編輯器這個類由什么字段和方法組成,然后使用這個類實例化對象。在我們使用委托的時候,也需要經(jīng)過這兩個階段,首先定義委托,告訴編輯器我們這個委托可以指向那些類型的方法,然后,創(chuàng)建該委托的實例。

  • 定義委托的方法如下:

    • delegate void IntMethodInvoker(int x);

    • 定義一個委托:IntMethodInvoker,這個委托可以指向什么類型的方法?

    • 這個方法要帶有一個 int類型的參數(shù),并且方法的返回值是void,定義一個委托要定義方法的參數(shù)和返回值,使用關(guān)鍵字delegate定義。

  • 定義案例:

    • delegate double TwoLongOp(long frist, long second);
    • delegate string GetAString();

委托使用

private delegate string GetAString();

static void Main(){
    int x = 40;
    GetAString firstStringMethod = new GetAString(x.ToString);
    Console.WriteLine(firstStringMethod());
}

  • 在這里我們首先使用GetAString委托聲明了一個類型叫做fristStringMethod,接下來使用new 對它進行初始化,使它引用到x中的ToString方法上,這樣firstStringMethod就相當(dāng)于x.ToString,我們通過firstStringMethod()執(zhí)行方法就相當(dāng)于x.ToString()

  • 通過委托示例調(diào)用方法有兩種方式:

    • fristStringMethod();
    • firstStringMethod.Invoke();

委托的賦值

  • GetAString firstStringMethod = new GetAString(x.ToString);
  • 只需要把方法名給一個委托的構(gòu)造方法就可以了
    GetAString firstStringMethod = x.ToString;
  • 也可以把方法名直接給委托的實例

簡單委托示例

  • 定義一個類MathsOperations里面有兩個靜態(tài)方法,使用委托調(diào)用該方法
class MathOperations{
    public static double MultiplyByTwo(double value){
        return value*2;
    }
    public static double Square(double value){
        return value*value;
    }
}
delegate double DoubleOp(double x);
static void Main(){
    DoubleOp[] operations={ MathOperations.MultiplyByTwo,MathOperations.Square };
    for(int i =0;i<operations.Length;i++){
        Console.WriteLine("Using operations "+i);
        ProcessAndDisplayNumber( operations[i],2.0 );
    }
}
static void ProcessAndDisplayNumber(DoubleOp action,double value){
    double res = action(value);
    Console.Writeline("Value :"+value+" Result:"+res);
}

Action委托和Func委托

  • 除了我們自己定義的委托之外,系統(tǒng)還給我們提供過來一個內(nèi)置的委托類型
  • ActionFunc
  • Action委托引用了一個void返回類型的方法,T表示方法參數(shù)
  • 先看Action委托有哪些
    • Action
    • Action<in T>
    • Action<in T1,in T2>
    • Action<in T1,in T2 .... inT16>
  • Func引用了一個帶有一個返回值的方法,它可以傳遞0或者多到16個參數(shù)類型,和一個返回類型
    • Func<out TResult>
    • Func<in T,out TResult>
    • Func<int T1,inT2,,,,,,in T16,out TResult>

案例1

  • delegate double DoubleOp(double x);
    如何用Func表示

  • Func<double,double>

案例2-對int類型排序

  • 對集合進行排序,冒泡排序
bool swapped = true;
do{
    swapped = false;
    for(int i =0;i<sortArray.Length -1;i++){
        if(sortArray[i]>sortArray[i+1]){
            int temp= sortArray[i];
            sortArray[i]=sortArray[i+1];
            sortArray[i+1]=temp;
            swapped = true;
        }
    }
}while(swapped);
  • 這里的冒泡排序只適用于int類型的,如果我們想對他進行擴展,這樣它就可以給任何對象排序。

案例2-雇員類

class Employee{
    public Employ(string name,decimal salary){
        this.Name = name;
        this.Salary = salary;
    }
    public string Name{get;private set;}
    public decimal Salary{get;private set;}
    public static bool CompareSalary(Employee e1,Employee e2){
        return e1.salary>e2.salary;
    }
}

案例2-通用的排序方法

public static void Sort<T>( List<T> sortArray,Func<T,T,bool> comparision ){
    bool swapped = true;
    do{
        swapped = false;
        for(int i=0;i<sortArray.Count-1;i++){
            if(comparision(sortArray[i+1],sortArray[i])){
                T temp = sortArray[i];
                sortArray[i]=sortArray[i+1];
                sortArray[i+1]=temp;
                swapped = true;
            }
        }
    }while(swapped);
}

案例2-對雇員類排序

static void Main(){
    Employee[] employees = {
        new Employee("Bunny",20000),        
        new Employee("Bunny",10000),        
        new Employee("Bunny",25000),        
        new Employee("Bunny",100000),       
        new Employee("Bunny",23000),        
        new Employee("Bunny",50000),
        };
    Sort(employees,Employee.CompareSalary);
    // 輸出
}

多播委托

  • 前面使用的委托都只包含一個方法的調(diào)用,但是委托也可以包含多個方法,這種委托叫做多播委托。使用多播委托就可以按照順序調(diào)用多個方法,多播委托只能得到調(diào)用的最后一個方法的結(jié)果,一般我們把多播委托的返回類型聲明為void

    • Action action1 = Test1;
    • action2+=Test2;
    • action2-=Test1;
  • 多播委托包含一個逐個調(diào)用的委托集合,如果通過委托調(diào)用的其中一個方法拋出異常,整個迭代就會停止。

  • 取得多播委托中所有方法的委托:

Action a1 = Method1;
a1+=Method2;

Delegate[] delegates=a1.GetInvocationList();
foreach(delegate d in delegates){
    //d();
    d.DynamicInvoke(null);
}
// 遍歷多播委托中所有的委托,然后單獨調(diào)用

匿名方法

  • 到目前為止,使用委托,都是先定義一個方法,然后把方法給委托的實例。但還有另外一種使用委托的方式,不用去定義一個方法,應(yīng)該說是使用匿名方法(方法沒有名字)。
Func<int,int,int> plus = delegate (int a,int b){
    int temp = a+b;
    return temp;
};
int res = plus(34,34);
Console.WriteLine(res);
  • 在這里相當(dāng)于直接把要引用的方法直接寫在了后面,優(yōu)點是減少了要編寫的代碼,減少代碼的復(fù)雜性

Lambda表達式-表示一個方法的定義

  • 從C#3.0開始,可以使用Lambda表達式代替匿名方法。只要有委托參數(shù)類型的地方就可以使用Lambda表達式。剛剛的例子可以修改為
  Func<int,int,int> plus = (a,b)=>{ int temp= a+b;return temp; };
  int res = plus(34,34);
  Console.WriteLine(res);
  • Lambda運算符“=>”的左邊列出了需要的參數(shù),如果是一個參數(shù)可以直接寫 a=>(參數(shù)名自己定義),如果多個參數(shù)就使用括號括起來,參數(shù)之間以,間隔

多行語句

  • 如果Lambda表達式只有一條語句,在方法快內(nèi)就不需要花括號和return語句,編譯器會自動添加return語句
Func<double,double> square = x=>x*x;
    // 添加花括號,return語句和分號是完全合法的
    Func<double,double> square = x=>{
        return x*x;
        }
  • 如果Lambda表達式的實現(xiàn)代碼中需要多條語句,就必須添加花括號和return語句。

Lambda表達式外部的變量

  • 通過Lambda表達式可以訪問Lambda表達式塊外部的變量。這是一個非常好的功能,但如果不能正確使用,也會非常危險。示例:
int somVal = 5;
Func<int,int> f = x=>x+somVal;
Console.WriteLine(f(3));//8
somVal = 7;
Console.WriteLine(f(3));//10
  • 這個方法的結(jié)果,不但受到參數(shù)的控制,還受到somVal變量的控制,結(jié)果不可控,容易出現(xiàn)編程問題,用的時候要謹(jǐn)慎。

事件

  • 事件(event)基于委托,為委托提供了一個發(fā)布/訂閱機制,我們可以說事件是一種具有特殊簽名的委托。
    什么是事件?
    事件(Event)是類或?qū)ο笙蚱渌惢驅(qū)ο笸ㄖl(fā)生的事情的一種特殊簽名的委托.
    事件的聲明
    public event 委托類型 事件名;
    事件使用event關(guān)鍵詞來聲明,他的返回類值是一個委托類型。
    通常事件的命名,以名字+Event 作為他的名稱,在編碼中盡量使用規(guī)范命名,增加代碼可讀性。
  • 為了更加容易理解事件,我們還是以前面的動物的示例來說明,有三只動物,貓(名叫Tom),還有兩只老鼠(Jerry和Jack),當(dāng)貓叫的時候,觸發(fā)事件(CatShout),然后兩只老鼠開始逃跑(MouseRun)。接下來用代碼來實現(xiàn)。(設(shè)計模式-觀察者模式)


    _15248252634703.png
Cat類和Mouse類
      class Cat
      {
          string catName;
          string catColor { get; set; }
          public Cat(string name, string color)
          {
              this.catName = name;
              catColor = color;
         }
         public void CatShout()
         {
             Console.WriteLine(catColor+" 的貓 "+catName+" 過來了,喵!喵!喵!\n");
             //貓叫時觸發(fā)事件
             //貓叫時,如果CatShoutEvent中有登記事件,則執(zhí)行該事件
             if (CatShoutEvent != null)
                 CatShoutEvent();
         }
         public delegate void CatShoutEventHandler();
         public event CatShoutEventHandler CatShoutEvent;
     }
     class Mouse
     {
         string mouseName;
         string mouseColor { get; set; }
         public Mouse(string name, string color)
         {
             this.mouseName = name;
             this.mouseColor = color;
         }
         public void MouseRun()
         {
             Console.WriteLine(mouseColor + " 的老鼠 " + mouseName + " 說:\"老貓來了,快跑!\"  \n我跑??!\n我使勁跑!!\n我加速使勁跑?。?!\n");
         }
     }
運行代碼結(jié)果
              Console.WriteLine("[場景說明]: 一個月明星稀的午夜,有兩只老鼠在偷油吃\n");
              Mouse Jerry = new Mouse("Jerry", "白色");
              Mouse Jack = new Mouse("Jack", "黃色");
              
              Console.WriteLine("[場景說明]: 一只黑貓躡手躡腳的走了過來\n");
              Cat Tom = new Cat("Tom", "黑色");
              Console.WriteLine("[場景說明]: 為了安全的偷油,登記了一個貓叫的事件\n");

             Tom.CatShoutEvent += new Cat.CatShoutEventHandler(Jerry.MouseRun);
             Tom.CatShoutEvent += new Cat.CatShoutEventHandler(Jack.MouseRun);


             Console.WriteLine("[場景說明]: 貓叫了三聲\n");
             Tom.CatShout();
             Console.ReadKey();

事件與委托的聯(lián)系和區(qū)別
  • 事件是一種特殊的委托,或者說是受限制的委托,是委托一種特殊應(yīng)用,只能施加+=,-=操作符。二者本質(zhì)上是一個東西。

  • event ActionHandler Tick; // 編譯成創(chuàng)建一個私有的委托示例, 和施加在其上的add, remove方法.

  • event只允許用add, remove方法來操作,這導(dǎo)致了它不允許在類的外部被直接觸發(fā),只能在類的內(nèi)部適合的時機觸發(fā)。委托可以在外部被觸發(fā),但是別這么用。

  • 使用中,委托常用來表達回調(diào),事件表達外發(fā)的接口。

  • 委托和事件支持靜態(tài)方法和成員方法, delegate(void * pthis, f_ptr), 支持靜態(tài)返方法時, pthis傳null.支持成員方法時, pthis傳被通知的對象.

  • 委托對象里的三個重要字段是, pthis, f_ptr, pnext, 也就是被通知對象引用, 函數(shù)指針/地址, 委托鏈表的下一個委托節(jié)點.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容