Skip to content

.net中委托和事件的学习-续

前面学习了委托就是可以把函数当参数传递的一种方式。在一种事情可以用各种不同方式来实现的场景中(比如用各种语言来问候)会省去非常多的判断逻辑。

按照Jimmy Zhang的文章,下面开始对《.net中委托和事件的学习》一文中的例子进行重构。重构成更符合实际应用的样子。

csharp
namespace Delegate
{
	//define delegate
	public delegate void GreetingDelegate(string name);

	//class GreetingManager
	public class GreetingManager
	{
		public void GreetPeople(string name,GreetingDelegate DoGreeting)
		{
			DoGreeting(name);
		}
	}
	class Program
	{
		private static void EnglishGreeting(string name)
		{
			Console.WriteLine("hello, "+name);
		}
		private static void ChineseGreeting(string name)
		{
			Console.WriteLine("你好, "+name);
		}

		static void Main(string[] args)
		{
			//it must be
			GreetingManager gm = new GreetingManager();
			gm.GreetPeople("welpher",EnglishGreeting);
			gm.GreetPeople("大家",ChineseGreeting):
		}
	}
}

根据上面学到的知识,我们可以定义一个委托变量,将方法绑定到这个变量上去,再结合面向对象的设计,对这个变量进行封装:

csharp
namespace Delegate
{
	public delegate void GreetingDelegate(string name);

	//class GreetingManager
	public class GreetingManager
	{
		public GreetingDelegate delegate1;

		public void GreetPeople(string name)
		{
			if(delegate1!=null)
			{
				delegate1(name);
			}
		}
	}

	class Program
	{
		//.......
		static void Main(string[] args)
		{
			GreetingManager gm = new GreetingManager();
			gm.delegate1 = EnglishGreeting;
			gm.delegate1+=ChineseGreeting;
			gm.GreetPeople("welpher");
		}
	}
}

这样就达到了我们的需求了,但是还是存在问题(存在的这个问题会引出另一个东西出来):

我们把delegate1封装到Manager类里面,这就出现一个问题,我们可以对它进行随意的赋值,你说这跟不封装有什么区别。而且对这个变量进行赋值时,第一次得用“=”,以后却要用“+=”,这让人如何是好,谁又知道自己使用的是第几次呢。

答案就是,用属性对这个字段进行封装。

于是顺理成章的,Event出场了。它封装的委托类型的变量:在类的内部,不管声明是public还是protected,它总是private的;在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。

根据上面所说的,我们对代码再进行改写:

csharp
namespace Delegate
{
	//define delegate
	public delegate void GreetingDelegate(string name);

	//class GreetingManager
	public class GreetingManager
	{
		public event GreetingDelegate DoGreet;
		public void GreetPeople(string name)
		{
			DoGreet(name);
		}
	}
	class Program
	{
		private static void EnglishGreeting(string name)
		{
			Console.WriteLine("hello, "+name);
		}
		private static void ChineseGreeting(string name)
		{
			Console.WriteLine("你好, "+name);
		}

		static void Main(string[] args)
		{

			GreetingManager gm = new GreetingManager();
			gm.DoGreet = EnglishGreeting;//编译错误
			gm.DoGreet += ChineseGreeting;
			gm.GreetPeople("welpher");
		}
	}
}

这段代码会在编译的时候出错。如上面所说,其实DoGreet会被编译成私有字段,所以不能直接赋值了。

事件和委托的编译代码

大神是借助Reflactor来对 event的声明语句做的探究。

csharp
public event GreetingDelegate DoGreet;

这段代码编译后会生成:

csharp
[MethodImpl(MethodImplOptions.Synchronized)]
public void add_DoGreet(GreetingDelegate value)
{
	this.DoGreet = (GreetingDelegate) Delegate.Combine(this.DoGreet,value);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_DoGreet(GreetingDelegate value)
{
	this.DoGreet = (GreetingDelegate) Delegate.Remove(this.DoGreet,value);
}

这两个方法对应“+=”和“-=”。在add_DoGreet()方法内部,调用了System.Delegate的Combine()静态方法,这个方法会将当前的变量添加到委托链表中。委托实际上是一个类。