委托:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
事件:其实事件是委托的一种封装,声明一个事件不过类似于声明一个私有的委托类型的变量而已,以防止其它对象从外部任意修改委托。
如何理解以上两者的区别?如下:
委托示例说明 :大家应该理解什么是委托,以及委托的使用方法。简单的委托使用:
2)在实际开发中,通常一个委托是作为对象的成员来声明的(而这个对象则称为委托的发布者)。则以上代码须修改如下:
事件示例说明 :
示例代码基本如以上委托示例代码,但包含事件的类声明如下:
事件:其实事件是委托的一种封装,声明一个事件不过类似于声明一个私有的委托类型的变量而已,以防止其它对象从外部任意修改委托。
如何理解以上两者的区别?如下:
委托示例说明 :大家应该理解什么是委托,以及委托的使用方法。简单的委托使用:
1、首先声明两个具体的操作
//声明第一个函数
public static void ConcreteFunctionOne(string input)
{
Console.WriteLine("Function One: " + input);
}
//声明第一个函数
public static void ConcreteFunctionTwo(string input)
{
Console.WriteLine("Function Two: " + input);
}
2、定义委托
//定义了可以代表方法的类型
public delegate void FunctionDelegate(string input);
3、使用委托
//定义了可以代表方法的类型
static void Main(string[] args)
{
FunctionDelegate fd = new FunctionDelegate(ConcreteFunctionOne);
fd += ConcreteFunctionTwo;
fd("This String.");
}
4、输出如下:
Function One: This String.
Function Two: This String.
以上代码展示了简单委托的应用,值得注意的有两点:
2)在实际开发中,通常一个委托是作为对象的成员来声明的(而这个对象则称为委托的发布者)。则以上代码须修改如下:
1、定义包含委托的类
//定义包含委托的类
public class ConcreteClass
{
public FunctionDelegate fd;
public void RunFunction(string input)
{
if (fd != null)
fd(input);
}
}
2、使用委托
//定义了可以代表方法的类型
static void Main(string[] args)
{
ConcreteClass cc = new ConcreteClass();
//cc.fd = new FunctionDelegate(ConcreteFunctionOne); 这种写法与下面的写法一样都可使用
cc.fd = ConcreteFunctionOne;
cc.fd += ConcreteFunctionTwo;
cc.RunFunction("This String.");
}
3、输出是相同的:
Function One: This String.
Function Two: This String.
而这里的代码又有稍许不同,就是注释中描述的写法,C#里两种都是可以被接受的。但是要注意的是第一个绑定的方法不一定使用new关键字或者“=”。如下写法:
static void Main(string[] args)
{
ConcreteClass cc = new ConcreteClass();
cc.fd += ConcreteFunctionOne; //也可以通过编译 运行时也不会报错
cc.fd += ConcreteFunctionTwo;
cc.RunFunction("This String.");
}
而事件在这里的处理则不同于委托,个人觉得这个是委托和事件最大的区别了,之后会的描述。
事件示例说明 :
示例代码基本如以上委托示例代码,但包含事件的类声明如下:
1、定义包含事件的类
//定义包含事件的类
public class ConcreteClass
{
public event FunctionDelegate fd;
public void RunFunction(string input)
{
if (fd != null)
fd(input);
}
}
可见与之前委托的声明区别仅仅是多了一个event关键字。
2、使用事件
static void Main(string[] args)
{
ConcreteClass cc = new ConcreteClass();
//cc.fd = new FunctionDelegate(); 编译器会报两个错误
//1、FunctiongDelegate不包含0个参数的构造函数
//2、ConcreteClass.fd只能在 ‘+=’ 和 ‘-=’的左边
//同样的cc.fd = ConcreteFunctionOne 也会报第二个错误
cc.fd += ConcreteFunctionOne;
cc.fd += ConcreteFunctionTwo;
cc.RunFunction("This String.");
}
而使用事件绑定方法时只能是以上代码写法。产生以上结果的原因,我们可以通过反编译工具对ConcreteClass进行反编译,通过研究反编译出来的代码发现,虽然在ConcreteClass类里将事件fd声明为Public,但fd会被编译为私有字段,不能被外界以赋值的方式访问。代码如下:
//反编译的类 非关键部分仍为C#代码
public class ConcreteClass
{
private FunctionDelegate fd; //事件被编译成私有委托成员
[MethodImpl(MethodImplOptions.Synchronized)]
public void add_fd(FuntionDelegate value)
{
this.fd = (FuntionDelegate)Delegate.Combine(this.fd, value);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_fd(FuntionDelegate value)
{
this.fd = (FuntionDelegate)Delegate.Remove(this.fd, value);
}
}
以上代码可以明确地说明事件是对委托的封装(无论事件声明为private还是public,都将被编译为一个private的委托)。而代码中的add_fd/remove_fd方法则分别对应于“+=”/“-=”。用于注册/取消注册的方法。其中add_fd方法中对System.Delegate的静态方法Combine()的调用,是将当前的变量(value:委托要注册的方法)添加到委托链表中去。
之前提到过委托中的注册方法是以链式结构存在的,而委托实际上也是作为一个类实现的,如下委托的声明:
//声明一个委托
public delegate void FunctionDelegate(string input);
编译器会将以上代码编译如下:
public class FunctionDelegate : System.MulticastDelegate{
public FunctionDelegate(object @object, IntPtr method);
public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);
public virtual void EndInvoke(IAsyncResult result);
public virtual void Invoke(string name);
}
也正是继承自System.MulticastDelegate类使委托具备了多播的特性(同一个委托可注册多个方法)。