C# 委託(delegate)、泛型委託和Lambda表達式


C# 委託(delegate)、泛型委託和Lambda表達式


文章目錄

    • # 什麼是委託
    • # 委託聲明、實例化和調用
      • 1、聲明
      • 2、委託的實例化
      • 3、委託實例的調用
      • 4、委託完整的簡單示例
    • #泛型委託
      • 1、Func委託
      • 2、Action委託
      • 3、Predicate委託
    • #匿名委託
    • #Lambda表達式
      • 1、表達式Lambda
      • 2、語句Lambda
      • 3、Lambda的主要用處
    • #多播委託
    • #參考:

# 什麼是委託

1、從數據結構來講,委託是和類一樣是一種用戶自定義類型。

2、委託是方法的抽象,它存儲的就是一系列具有相同參數和返回類型的方法的地址。調用委託的時候,委託包含的所有方法將被執行。

委託是一種特殊的類,因此委託的聲明與類的聲明方法類似,在任何可以聲明類的地方都可以聲明委託。委託聲明用delegate關鍵字,同時委託要指明方法參數和返回值,寫法與方法類似。綜合類的聲明和方法的聲明,委託聲明寫成如下形式:

[訪問修飾符] delegate 返回值類型 委託名 (形參列表);

<code>public delegate void MyDel();//定義了一個委託MyDel,它可以註冊返回void類型且沒有參數的函數
public delegate void MyDel1(string str);//定義了一個委託MyDel1,它可以註冊返回void類型且有一個string作為參數的函數
public delegate int MyDel2(int a,int b);//定義了一個委託MyDel2,它可以註冊返回int類型且有兩個int作為參數的函數

/<code>

2、委託的實例化

與普通類的使用方法相同,聲明瞭委託之後,我們必須給委託傳遞一個具體的方法,才能在運行時調用委託實例。委託實例包含了被傳遞給它的方法的信息,在運行時,調用委託實例就相當於執行它當中的方法。

委託實例化格式如下:

委託類名 委託實例名 = new 委託類名(Target) ;

其中,委託實例名是自定義的名稱,Target是要傳入的方法的名稱。注意,Target是方法的引用,不能帶()。帶()的話是該方法的調用。區分引用和調用。
委託的實例化還有一種簡單的方法:

委託類名 委託實例名 = Target;

在需要委託實例的地方直接傳入Target引用即可,C#編譯器會自動根據委託類型進行驗證,這稱為“委託推斷”。

<code>MyDel2 testDel=new MyDel2(Add);
MyDel2 testDel1 = Add;/<code>

3、委託實例的調用

委託實例等價於它當中實際方法,因此可以使用反射的Invoke()方法調用委託實例,也可以直接在委託實例後加上()進行調用。

<code>int num = testDel(1,2);
int num1 = testDel.Invoke(1, 2);/<code>

4、委託完整的簡單示例

<code>namespace delegateTest
{
public delegate int MyCalculator(int num1, int num2);
class Program
{
static void Main(string[] args)
{
MyCalculator myCal=new MyCalculator(Add);
int addNum= myCal(1,2);

MyCalculator myCal1 = Sub;
int subNum = myCal1.Invoke(1, 2);

Console.WriteLine("addNum:{0},subNum:{1}", addNum, subNum);

int calNum = Calculate(1, 2, Add);
Console.WriteLine("calNum:{0}", calNum);
}

static int Add(int num1, int num2)
{
Console.WriteLine("num1 + num2={0}",num1 + num2);
return num1 + num2;
}
static int Sub(int num1, int num2)
{
Console.WriteLine("num1 - num2={0}", num1 - num2);
return num1 - num2;
}
static int Calculate(int num1,int num2,MyCalculator calDel)
{
return calDel(num1,num2);
}
}
}/<code>

控制檯打印結果:

<code>num1 + num2=3
num1 - num2=-1
addNum:3,subNum:-1
num1 + num2=3
calNum:3/<code>

#泛型委託

我們每次要使用一個委託時,都需要先聲明這個委託類,規定參數和返回值類型,然後才能實例化、調用。為了簡化這個過程, .NET 框架為我們封裝了三個泛型委託類,因此大部分情況下我們不必再聲明委託,可以拿來直接實例化使用,方便了我們的日常寫代碼。

這三種泛型委託包括:Func委託、Action委託和Predicate委託。

1、Func委託

Func委託代表著擁有返回值的泛型委託。Func有一系列的重載,形式如 Func,其中TResult代表委託的返回值類型,其餘均是參數類型。只有一個T時,即Func,代表該委託是無參數的。.NET封裝了最多16個輸入參數的Funct<>委託。

需要特別注意的是,若方法沒有返回值,即返回 void ,由於 void 不是數據類型,因此不能定義Func委託。返回 void 的泛型委託見下文的Action。

Func的使用方法與一般的委託相同。例如上面的案例可改寫如下:

<code>namespace delegateTest
{
class Program

{
static void Main(string[] args)
{
int calNum = Calculate(1, 2, Sub);
Console.WriteLine("calNum:{0}", calNum);// -1
}
static int Calculate(int num1, int num2, Func calDel)
{
return calDel(num1,num2);
}
static int Sub(int num1, int num2)
{
Console.WriteLine("num1 - num2={0}", num1 - num2);
return num1 - num2;
}
}
}
/<code>

2、Action委託

Action委託代表返回值為空 void 的委託,它也有一些列重載,最多擁有16個輸入參數。用法與Func相同。

<code>namespace delegateTest
{
class Program
{
static void Main(string[] args)
{
\t\t\tDoSome("hello",Say);// hello
}
static void DoSome(string str,Action<string> doAction)
{
doAction(str);
}
static void Say(string str)
{
Console.WriteLine(str);
}
}
}/<string>/<code>

3、Predicate委託

這個一般用的較少,它封裝返回值為bool類型的委託,可被Func代替。

#匿名委託

採用匿名方法實例化的委託稱為匿名委託。

每次實例化一個委託時,都需要事先定義一個委託所要調用的方法。為了簡化這個流程,C# 2.0開始提供匿名方法來實例化委託。這樣,我們在實例化委託時就可以 “隨用隨寫” 它的實例方法。

使用的格式是:

委託類名 委託實例名 = delegate (args) { 方法體代碼 } ;

這樣就可以直接把方法寫在實例化代碼中,不必在另一個地方定義方法。當然,匿名委託不適合需要採用多個方法的委託的定義。
使用匿名方法,以上代碼可改寫為:

<code>MyCalculator myCal2 = delegate(int num1, int num2)
{
\t//打印匿名方法的名字
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); // <main>b__0
return num1 + num2;
};
int num11= myCal2(1,2);//3/<main>/<code>

需要說明的是,匿名方法並不是真的“沒有名字”的,而是編譯器為我們自動取一個名字。

#Lambda表達式

縱然匿名方法使用很方便,可惜她很快就成了過氣網紅,沒能領多長時間的風騷。如今已經很少見到了,因為delegate關鍵字限制了她用途的擴展。自從C# 3.0開始,她就被Lambda表達式取代,而且Lambda表達式用起來更簡單。Lambda表達式本質上是改進的匿名方法。

1、表達式Lambda

當匿名函數只有一行代碼時,可採用這種形式。例如:

<code>MyCalculator myCal = (num1, num2) =>  num1 + num2;
int num = myCal(1, 2);// 3/<code>

其中=>符號代表Lambda表達式,它的左側是參數,右側是要返回或執行的語句。參數要放在圓括號中,若只有一個參數,為了方便起見可省略圓括號。有多個參數或者沒有參數時,不可省略圓括號。
相比匿名函數,在表達式Lambda中,方法體的花括號{}和return關鍵字被省略掉了。

2、語句Lambda

當匿名函數有多行代碼時,只能採用語句Lambda。

<code>MyCalculator myCal = (int num1, int num2)=>
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);
return num1 + num2;
};
int num = myCal(1, 2);// 3/<code>

語句Lambda不可以省略{}和return語句。

3、Lambda的主要用處

實際中用到Lambda表達式的地方大都是委託,例如linq的對集合類的擴展查詢方法;
很多架構的搭建需要調用自定義方法,也離不開委託;
事件機制是基於委託的;
等等。

#多播委託

實例化委託時必須將一個匹配函數註冊到委託上來實例化一個委託對象,但是一個實例化委託不僅可以註冊一個函數還可以註冊多個函數,註冊多個函數後,在執行委託的時候會根據註冊函數的註冊先後順序依次執行每一個註冊函數。
函數註冊委託的原型:

+=new ();

注意:委託必須先實例化以後,才能使用+=註冊其他方法。如果對註冊了函數的委託實例從新使用=號賦值,相當於是重新實例化了委託,之前在上面註冊的函數和委託實例之間也不再產生任何關係。 有+=註冊函數到委託,也有-=解除註冊;

-=new ();

注意:如果在委託註冊了多個函數後,如果委託有返回值,那麼調用委託時,返回的將是最後一個註冊函數的返回值。

<code>MyCalculator multiCal=new MyCalculator(Add);
multiCal += Sub;
int num1 = multiCal(1, 2); // -1
multiCal -= Sub;
int num2 = multiCal(1, 2); // 3/<code>


C# 委託(delegate)、泛型委託和Lambda表達式


分享到:


相關文章: