前面学习了委托就是可以把函数当参数传递的一种方式。在一种事情可以用各种不同方式来实现的场景中(比如用各种语言来问候)会省去非常多的判断逻辑。
按照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()静态方法,这个方法会将当前的变量添加到委托链表中。委托实际上是一个类。