1. 程式人生 > >c#學習筆記之委託

c#學習筆記之委託

委託

最近自己在除錯C#專案,發現經常可以看到委託和lambda表示式,各種花裡胡哨的寫法把我給整的雲裡霧裡的,於是自己特意花了一點功夫來整理關於delegate的相關知識,方便自己日後查閱。

何為委託

委託是.NET中的定址方法,和C++的函式指標很像;但是委託是型別安全的類,定義了返回型別和引數型別,也就是說委託一種使用者自定義的型別,和普通的類一樣;

委託的使用

宣告委託

    delegate void IntMethodInvoker(int x);    //定義委託IntMethodInvoker, 引數是int,返回型別void

我們可以把委託當做是一件事情,就是給方法名字和返回型別指定一個別名

從上面的例子可以總結出宣告委託的模板如下:

  • delegate + 返回型別 + 委託型別名 + 引數列表

委託的使用

例項化委託,並且需要對其進行初始化,這樣可以類似地當做一個變數使用了,下面給出一個具體的例子來說明如何使用委託。

    using System;
    
    namespace DelegateSamples{
    
        delegate double DoubleOp(double x);    // 宣告一個委託型別
        class MathOperations
        {
            public static doube MultiplyByTwo(double value)
                return value * 2;
    
            public static double Square(double value)
                return value * value;
        }
        
        class Program
        {
            static void Main()
            {
                // 宣告一個委託陣列,就像普通陣列一樣
                DoubleOp[] operations = {MathOperations.MultiplyByTwo, MathOperations.Square};
                
                for(int i = 0; i < operations.length; i++)
                {
                    Console.WriteLine("using operations[{0}]:", i);
                    ProcessAndDisplayNumber(operations[i], 3.2);
                    Console.WriteLine();
                }
            }
        
            static void ProcessAndDisplayNumber(DoubleOp action, double value)
            {
                double res = action(value);         // 呼叫action實際封裝的方法
                Console.WrireLine("Value is {0}, result of operation is {1}", value, res);
            }
         }

在呼叫委託的時候,最後判斷委託是否為null,不然可能會引起異常

Action和Func委託

Action<T>委託表示引用一個void返回型別的方法,Action<T>有很多種變體,Action<in T>呼叫帶一個引數的方法,沒有泛型引數的Action類呼叫不帶引數的方法;
Func<T>委託類似,表示引用帶返回型別的方法,Func<T>也有很多變體,Func<Out TResult>表示呼叫帶返回型別但沒有引數的方法,Func<in T, out TRsult>呼叫一個帶引數的方法,以此類推;

// 宣告一個返回double型別,並且帶一個double引數的委託
Func <double, double> operations = {MathOperations.MultiplyByTwo, MathOperations.Square};

// 注意這個方法和上面方法的不一樣,利用了Func類
static void ProcessAndDisplayNumber(Func <double, double> action, double value)
{
        double res = action(value);         // 呼叫action實際封裝的方法
        Console.WrireLine("Value is {0}, result of operation is {1}", value, res);
}

多播委託

在之前的例子中,每個委託都只含有一個方法呼叫,也就是說呼叫多少次委託就是呼叫多少次方法。

但是,一個委託也可以包含多個方法,這種委託叫做多播委託,因此,這種委託必須返回void,不然返回的資料是不對的

    Action<double> operations = MathOperations.MultiplyByTwo;
    operations += MathOperations.Square;    //這裡在給委託新增一個方法
    

對於多播委託的使用,呼叫方法鏈的順序並沒得到正式定義,這要求我們儘量避免依賴特定順序呼叫方法的程式碼;

從上面的例子可以知道,多播委託是可以識別+,+=,-,-=這些符號的,下面給出具體的示意:

  • +=: 表示委託新增一個方法
  • -=: 表示委託減少一個方法

匿名方法

在上面的例子中,委託所呼叫的方法,都是我們自己預先寫好的,但是委託也可以使用匿名方法,也就是將匿名方法用作委託的引數,這在例項化委託的會有一些不一樣的,具體如下面例子所示:

    string name = "";
    
    // Func<string, string>委託接受一個string引數,返回一個string,注意匿名方法的實現,delegate開頭
    Func<string, string> anonDel = delegate(string param){
        param += name;
        param += "just for test";
        return param;
    };

使用匿名方法需要遵守的規則:

  1. 在匿名方法中不能使用跳轉語句(goto、break、continue),匿名方法外部的語句也不能跳轉到匿名方法內部;
  2. 在匿名方法中不能訪問不完全的程式碼,也不能訪問在匿名方法外部定義的ref、out引數;

在C#3.0之後,lambda表示式代替了匿名方法,寫起來感覺更舒服;

Lambda表示式

lambda表示式主要是用來替代匿名方法的,因為顯然委託知道他自己需要呼叫的是什麼方法,不需要宣告delegate關鍵字,在引數和方法體之間插入=>,表示“goes to",具體示例如下所示:

    string name = "";
    
    // Func<string, string>委託接受一個string引數,返回一個string,這裡使用lambda表示式
    Func<string, string> anonDel = param => {
        param += name;
        param += "just for test";
        return param;
    };
   // 這樣的lambda表示式是不是很優雅,就是除錯程式的時候會有點煩
   

為了更好地使用lambda表示式,方便我們自己寫程式碼寫的花裡花哨的,就對其多做一點介紹:

引數

  1. 如果只有一個引數,就只需要寫出引數名字就行

     Func<string, string> oneParam = s => String.Format("change to Upper {0}", s.ToUpper());
  2. 使用多個引數,就把引數用寫在()裡

    Func<double, double, double> twoParams= (x, y) => x * y;    // 返回x*y

在使用多個引數的時候,我覺得還是在引數前面加上型別比較好,便於理解,例如(double x, double y) => x * y;

多行程式碼

  1. 只有一條語句,方法裡面不需要{}和return,編譯器會自動新增一個隱式的return

     Func<double, double, double> result = x, y => x * y;
  2. 含有多條語句,需要加上{}和return

    string lastname = "Alex";
    Func<string ,string> printName = name =>
        {
            name += lastname;
            return String.Format("Being Upper: {0}",name);
        }