C#中委託、匿名方法、Lambda表達式的相關解釋

0x01定義一個委託,相當於定義一個可以存儲方法的特殊變量類型

下面我們看具體的代碼,通過代碼更好理解

delegate void IntMethodInvoker(int x);

這行代碼就是聲明一個委託,其中delegate是關鍵字,表示聲明一個委託,void是要存儲的方法的返回類型,IntMethodInvoker是聲明的委託類型名字,結合最開始的那句話就是自定義的類書變量類型名,int x則是要存儲的方法的返回類型


這樣一個委託可以存放哪些方法呢?

只要返回類型一致參數列表一致的方法都能存儲

在這個例子中,就是返回類型爲void,有一個int形參的方法

void test(int t)
{
    Console.WriteLine("Just a test" + t )
}

如這樣的test方法就可以存儲到IntMethodInvoker委託的實例中

委託是一種特殊類型的對象,定義好委託之後就可以創建它的實例瞭然後存儲方法

IntMethodInvoker DelegateTest = test;

這行代碼即將test方法的地址存放到IntMethodInvoker委託的實例DelegateTest中,在以後只需要調用DelegateTest就能執行test方法。

DelegateTest(32);

這行代碼與test(32)效果相同


委託可以添加多個方法引用,添加多個方法引用的委託叫多播委託

添加多個方法引用使用+=,若要刪除添加的方法引用使用-=

0x02使用委託可以將方法當作參數傳遞

既然前面說了委託類似於定義一個可以存儲方法的特殊變量類型,那麼我們也可以將這個特殊類型作爲方法的形參,將一個方法引用添加到委託實例中,然後將委託實例作爲另一個接受委託類型參數的方法的實參,就達到了將方法傳遞給另一個方法的效果

class Program
{

    static void Main(string[] args)
    {
        Test test = new Test();
        Test.TestDelegate testDelegate = new Test.TestDelegate(test.Fun);

        test.KK(testDelegate);
    }
}
class Test
{
    public delegate void TestDelegate();

    public void Fun()
    {
        Console.WriteLine("這是一個簡單的方法!");
    }
    public void KK(TestDelegate testDelegate)
    {
        Console.WriteLine("這是一個接收委託的方法");
        testDelegate();
    }
}

這是一個將委託作爲參數,將一個方法傳遞給另一個方法的例子

在這個例子中,先聲明瞭一個Test實例,然後聲明瞭一個TestDelegate委託實例testDelegate

testDelegate委託包含了test.Fun()的引用,這時並沒有調用test.Fun(),只是保存了引用

接下來執行test.KK(testDelegate),這時testDelegate委託作爲形參傳遞到了KK方法中

KK方法在調用testDelegate();時,委託執行Fun方法

所以最後的執行結果是

這是一個接收委託的方法
這是一個簡單的方法!

0x03Func<T>Action<T>

委託非常常用,所以.Net內置了兩個委託類型,這樣我們就不用自己去聲明委託了

Action<T>泛型委託,表示引用一個void返回類型的方法。這個委託存在不同的變體,可以接受需要0 ~ 16個參數的方法

Func<T>泛型委託,與Action<T>相似,但是表示引用一個有一個返回值類型的方法。Func<T>同樣可以接收0 ~ 16個參數的方法,默認最後一個是返回類型,即Fun<T>最多有十七位參數

Func<string,string,int>

這個Func表示可以接收一個參數列表爲兩個string,返回一個int的方法。

0x04Lambda和匿名方法

匿名方法:

Func<string, string> anonDel = delegate (string param)
{
    param += mid;
    param += " and this was added to the string.";
    return param;
};

匿名方法的作用:

委託是存儲方法的引用,也就是說,要用委託,就必須要先有方法,但是實際上有很多時候委託所存儲的方法引用只會在委託或者事件(事件基於委託)中使用,這時候我們單獨定義一個方法很麻煩,這時候就可以使用匿名方法,上方就是一個匿名方法,我們看右邊

delegate (string param)
{
    param += mid;
    param += " and this was added to the string.";
    return param;
};

這個就是匿名方法,使用delegate關鍵字,如果我們把delegate換成一個普通的名字

KK (string param)
{
    param += mid;
    param += " and this was added to the string.";
    return param;
};

這就是一個普通的方法(結尾的分號是Fun委託賦值語句的結尾),所以匿名方法就是一個普通的方法將方法名去掉換成delegate,以達到簡化代碼的目的

Func<string, string> anonDel = 

delegate (string param)
{
    param += mid;
    param += " and this was added to the string.";
    return param;
};

這樣看是不是很好理解了

Lambda表達式:

在我的理解中,Lambda表達式其實是爲了簡化匿名方法

我們依然用上面的代碼

//匿名方法
delegate (string param)
{
    param += mid;
    param += " and this was added to the string.";
    return param;
}


//Lambda表達式
(string pram) =>
{
    param += mid;
    param += " and this was added to the string.";
    return param;
}

這兩個是等價版本, **=>**就是Lambda運算符,現在我們看它的兩邊是什麼,左邊是參數列表,右邊是方法體。

這個Lambda其實有一點複雜了,通常我們遇到的Lambda會更簡化

  • 如果只有一個參數,只需要參數名就足夠了,即這個Lambda中,可以將(string pram)改寫爲pram
  • 如果方法體只有一行語句,那麼在方法塊中就不需要花括號和return語句

舉個例子

Func<double,double> square = (double x) =>
{
    return x*x;
};

這個Lambda表達式可以簡寫爲

Func<double,double> square = x => x*x

Lambda表達式的閉包問題

這裏就一句話,在Lambda表達式使用了外部的變量(不是Lambda表達式內的變量),外部的變量的值在調用時決定,而不是定義Lambda表達式時決定

int someVal = 5;
Func<int, int> ZZ = x => x + someVal;
someVal = 7;
Console.WriteLine(ZZ(3));

最後輸出的時7,因爲ZZ委託調用Lambda表達式時someVal已經等於7了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章