委托給了C#操作函數的靈活性,我們可使用委托像操作變量一樣來操作函數,其實這個功能並不是C#的首創,早在C++時代就有函數指針這一說法,而在我看來委托就是C#的函數指針,首先先簡要的介紹一下委托的基本知識:
委托的定義
委托的聲明原型是
delegate <函數返回類型> <委托名> (<函數參數>)
例子:public delegate void CheckDelegate(int number);//定義了一個委托CheckDelegate,它可以注冊返回void類型且有一個int作為參數的函數
這樣就定義了一個委托,但是委托在.Net內相當於聲明了一個類(在後面的代碼中會講到確實如此),類如果不實例化為對象,很多功能是沒有辦法使用的,委托也是如此.
委托的實例化
委托實例化的原型是
<委托類型> <實例化名>=new <委托類型>(<注冊函數>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//用函數CheckMod實例化上面的CheckDelegate 委托為_checkDelegate
在.Net 2.0開始可以直接用匹配的函數實例化委托:
<委托類型> <實例化名>=<注冊函數>
例子:CheckDelegate _checkDelegate=CheckMod;//用函數CheckMod實例化上面的CheckDelegate 委托為_checkDelegate
現在我們就可以像使用函數一樣來使用委托了,在上面的例子中現在執行_checkDelegate()就等同於執行CheckMod(),最關鍵的是現在函數CheckMod相當於放在了變量當中,它可以傳遞給其它的CheckDelegate引用對象,而且可以作為函數參數傳遞到其他函數內,也可以作為函數的返回類型
事件是委托的一種特殊形式,當發生有意義的事情時,事件處理對象通知過程。
一.C語言中的函數指針
想要理解什麼是委托,就要先理解函數指針的概念。所謂函數指針,就是指向函數的指針(等於沒說-.-)。比如我定義了兩個函數square和cube分別用於計算一個數的平方和立方,我再定義函數指針calcu,然後我讓calcu指向square,那麼調用calcu時就相當於調用了square函數(注意,此處函數指針接受的參數類型及個數要與函數一致)。很好理解吧?不多說,上代碼。
#include <stdio.h>
void
square(
int
x) { printf(
"square of %d is %d\n"
,x,x*x); }
void
cube(
int
x) { printf(
"cube of %d is %d\n"
,x,x*x*x); }
int
main()
{
void
(*calcu)(
int
x);
calcu=square;
calcu();
return
;
}
二.C#中委托的實質
委托又名委托類型,為什麼C#弄出這個東西?因為C#是一門比較安全的語言,不允許操作指針,於是我們不能定義函數指針。但想要達到相同的效果,於是定義了委托類型。所謂委托類型,其本質就是C中的指針類型。於是代碼變成了這樣:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Threading.Tasks;
namespace
Delegate
{
class
Program
{
static
void
square(
int
x) { Console.WriteLine(
"square of {} is {}"
, x, x * x); }
static
void
cube(
int
x) { Console.WriteLine(
"cube of {} is {}"
, x, x * x * x); }
delegate
void
math(
int
x);
//定義委托類型
static
void
Main(
string
[] args)
{
math calcu;
calcu += square;
calcu();
Console.ReadKey();
}
}
}
可以看出,定義委托類型math實際上就相當於定義了void*類型。而委托類型實例化得到的calcu實際上就是函數指針。(說句題外話:定義函數(方法)時要加上static是因為調用函數時並未實例化,只有靜態方法能夠直接通過類調用)。
三.委托的使用方法
我們在上述代碼19行後面加上一行代碼 calcu+=cube; 運行會發現,square和cube均被調用。可以看出,符號 += 表示綁定方法到委托變量,同理符號 -= 表示取消綁定。可以理解為calcu是void **類型,即它指向了一個數組,數組中的每一項都是函數指針類型,每次調用calcu時,遍歷此數組,即依次調用每個綁定的方法。
四.封裝與事件的引入
下面我們要用面向對象的思想將上述代碼進行封裝,使其變清晰。
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Threading.Tasks;
namespace
Delegate
{
public
delegate
void
math(
int
x);
public
class
Calcu
{
public
math calcu;
}
class
Program
{
static
void
square(
int
x) { Console.WriteLine(
"square of {} is {}"
, x, x * x); }
static
void
cube(
int
x) { Console.WriteLine(
"cube of {} is {}"
, x, x * x * x); }
static
void
Main(
string
[] args)
{
Calcu c =
new
Calcu();
c.calcu += square;
c.calcu += cube;
c.calcu();
Console.ReadKey();
}
}
}
由於委托變量是public的,封裝的程度很低,在外部可以任意修改。為了改進這個問題,C#引入了事件。
所謂事件,實際上還是委托的實例化,只是其內部多了一些定義,多了一些限制。其一,事件實際上聲明了一個private類型的委托變量,因此在類外無法直接調用。
於是我們將上述代碼的第12行改成這樣:
public event math calcu;
運行之後25行報錯了,因為calcu是private的,不能直接調用。但23,24行並沒有報錯。那麼問題來了,為什麼我們可以用+=來給calcu綁定方法呢?
因為其二,事件還幫我們干了一件事情,就是定義了綁定方法和取消綁定方法的函數,它們是public的,並且將運算符+=,-=重載,和這兩個函數對應。
好了,現在我們要寫一個接口函數來完成計算:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Threading.Tasks;
namespace
Delegate
{
public
delegate
void
math(
int
x);
public
class
Calcu
{
public
event
math calcu;
public
void
calculate(
int
x)
{
calcu(x);
}
}
class
Program
{
static
void
square(
int
x) { Console.WriteLine(
"square of {} is {}"
, x, x * x); }
static
void
cube(
int
x) { Console.WriteLine(
"cube of {} is {}"
, x, x * x * x); }
static
void
Main(
string
[] args)
{
Calcu c =
new
Calcu();
c.calcu += square;
c.calcu += cube;
c.calculate();
Console.ReadKey();
}
}
}
至此,基本概念已經清晰。
想來,使用事件會讓人不得不將對象封裝起來,這應該就是面向對象思想的體現吧。
以上內容是針對C#委托與事件初探的相關知識,希望對大家有所幫助。