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;
}
}
}