1.談談函數指針
一個函數在編譯時被分配給一個入口地址。這個入口地址就稱爲“函數的指針”。用一個指針變量指向函數,然後通過該指針變量調用此函數,這個指針變量就稱爲“指向函數的指針”。
{
int Max(int,int);
//聲明一個函數指針
int (*p)(int,int);
int a,b,c;
//
//把Max函數的入口地址賦給指針變量p
p=Max;
//函數指針的形式調用Max函數
c=(*p)(a,b);
}
如果給p賦不同的值(不同的函數地址),那麼調用者就能調用不同的函數;賦值可以發生在運行時,從而實現動態綁定。這裏的調用者即我們要談的回調函數。
{
int Max(int,int);
int Min(int,int);
//
int (*p)(int,int);
//p=Min;
p=Max;
//傳遞函數地址給調用者
Caller(p);
}
//回調函數
void Caller(int (*ptr)(int,int))
{
//調用ptr指向的函數
ptr();
}
2.什麼是委託
以下內容來自:http://www.cnblogs.com/WuCountry/archive/2006/11/29/576030.html
先來看下面的代碼:
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
bool m_isRight = false;
object m_obj = m_isRight?MyWrite("true"):MyWrite("false");
Console.Write(m_obj);
}
static private int MyWrite(object i_string)
{
Console.Write(i_string);
return i_string.ToString().Length;
}
}
}
如果真要給m_obj對象賦予函數MyWrite(),會怎麼樣呢?
把函數當成變量賦給對象,我們首先得解決三個問題:
1、如果可以對一個對象賦函數值,如何區別不同的函數;
2、如何給這個對象賦函數值;
3、如何用這個對象調用原來的函數;
對於第一個問題,首先我們應認識到c#中是可以對一個對象賦函數值的;解決這個問題的辦法是先對該對象聲明,聲明它可以被什麼樣的函數來賦值,而這個對象聲明在C#裏的學名就是委託。這和上面講到的函數指針十分類似(其實委託的內部機制比函數指針複雜的多)。
現在我們可以聲明如下的委託來解決問題一:
//
MyDelegate m_delegate = new MyDelegate(MyWrite);
//MyWrite函數如下,它是滿足委託的申明的。
static private int MyWrite(object i_string)
{
Console.Write(i_string);
return i_string.ToString().Length;
}
這樣就完美的解決了第一個問題。
OK,第二個問題。如何給這個對象m_delegate賦函數值?其實上面在實例化委託對象時,已經對其賦值MyWrite,因此它已經具有了MyWrite函數的功能。只有這種方法對其進行賦值麼?不,另一種賦值形式就是事件。
我們再來看第三個問題,這個問題看下面的代碼就清楚了:
namespace ConsoleApplication1
{
class Class1
{
//先申明一個委託對象。
delegate int MyDelegate(object i_object);
[STAThread]
static void Main(string[] args)
{
MyDelegate m_delegate = new MyDelegate(MyWrite);
m_delegate("This is a delegate object to call the raw function.");
}
//該函數是滿足上面委託對象的申明的。
static private int MyWrite(object i_string)
{
Console.Write(i_string);
return i_string.ToString().Length;
}
}
}
3.事件與事件委託
事件是對象發送的消息,以發信號通知操作的發生。操作可能是由用戶交互(例如鼠標單擊)引起的,也可能是由某些其他的程序邏輯觸發的。引發事件的對象稱爲事件發送方。捕獲事件並對其作出響應的對象叫做事件接收方。(MSDN)
事件是基於委託的,而由上面的定義可以知道我們將要遇到的問題是:
1、如何動態的(運行時)對“特殊委託”賦函數值,怎樣實現?
2、運行時,如何知道“特殊委託”已經被賦過值及如何賦值?
3、能否在“特殊委託”上添加多個函數值?如果可以,如何刪除?
相信大家已經知道這個“特殊委託”就是我們要談的事件。來看一下它的聲明:
public event MyDelegate m_myevent;//聲明事件(第一個問題)
public MyDelegate m_mydelegate;//聲明委託
是不是很像?我們來看看它們的區別:
1、事件不能直接把函數當值一樣賦給它的委託;而委託可以直接賦函數;
2、事件只能把一個實例的委託當值賦給它;也就是說事件是用來管理委託的,進而管理函數。因爲一個實例化的委託肯定有一個函數與之對應。
3、在一個事件上可以動態添加或刪除委託;而委託上不能動態添加或刪除函數。
OK,我們來看看第二個問題。如何賦值?它的賦值有點怪:
m_myevent += m_mydelegate;
這裏也正好說明了事件是動態管理委託的。
那麼又如何刪除?m_myevent -= m_mydelegate;沒有委託的情況下可以刪除麼?答案是肯定的。
最後看一個完整的例子(從中可以知道如何判斷事件是否已賦過值):
namespace ConsoleApplication1
{
class Class1
{
//先聲明一個委託對象。
delegate int MyDelegate(object i_object);
//聲明一個事件對象
static event MyDelegate m_myevent;
[STAThread]
static void Main(string[] args)
{
//實例化委託
MyDelegate m_delegate = new MyDelegate(MyWrite);
m_delegate("This is a delegate object to call the raw function.");
//實例化的委託賦值給事件
m_myevent += m_delegate;
m_myevent += new MyDelegate(MyWrite);
m_myevent +=new MyDelegate(Class1_m_myevent);
//判斷事件是否已賦過值
if(m_myevent!=null)
{
m_myevent("This is a event to call the funcaion on the delegate.");
}
}
//該函數是滿足上面委託對象的申明的。
static private int MyWrite(object i_string)
{
Console.WriteLine(i_string);
return i_string.ToString().Length;
}
private static int Class1_m_myevent(object i_object)
{
Console.WriteLine(i_object);
return 0;
}
}
}