.NET中的委托和事件

委托:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

事件:其实事件是委托的一种封装,声明一个事件不过类似于声明一个私有的委托类型的变量而已,以防止其它对象从外部任意修改委托。

如何理解以上两者的区别?如下:
委托示例说明 :大家应该理解什么是委托,以及委托的使用方法。简单的委托使用:

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.

以上代码展示了简单委托的应用,值得注意的有两点:

1)同一个委托可以绑定多个与委托声明相符的方法(方法可以是静态方法,也可以是实例方法)。这是由于委托绑定的实现是以链式存储的。
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类使委托具备了多播的特性(同一个委托可注册多个方法)。
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭