C#深入理解委托和事件的区别
1、委托
class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher("篮球先锋报");
Observer observerA = new Observer("老A");
publisher.Magazine += observerA.RecvMagazine;
Observer observerB = new Observer("老B");
publisher.Magazine += observerB.RecvMagazine;
publisher.PublishMagezine();
//或者使用下面的方式 区别就是一个在定义的内部触发,一个在外部触发
publisher.Magazine?.Invoke(publisher.magazineName);
Console.ReadKey();
}
}
public class Observer
{
private string name;
public Observer(string name)
{
this.name = name;
}
public void RecvMagazine(string message)
{
Console.WriteLine($"{this.name} recv {message}, 仔细读了一番");
}
}
public class Publisher
{
public string magazineName;
public Publisher(string magazineName)
{
this.magazineName = magazineName;
}
public delegate void MagazineDelegate(string message);
public MagazineDelegate Magazine;
public void PublishMagezine()
{
Magazine?.Invoke(this.magazineName);
}
}
可以看出委托有一个特点,我们一般在一个类中声明一个委托对象( public MagazineDelegate Magazine;),然后再这个类的外部订阅它(publisher.Magazine += observerA.RecvMagazine;)。但是我们在触发这个委托的时候,可以在内部中触发(就是用一个方法封装起来公开给外部调用):
public void PublishMagezine()
{
Magazine?.Invoke(this.magazineName);
}
也可以在外部直接触发:
publisher.Magazine?.Invoke(publisher.magazineName);
2、事件
class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher("篮球先锋报");
Observer observerA = new Observer("老A");
publisher.Magazine += observerA.RecvMagazine;
Observer observerB = new Observer("老B");
publisher.Magazine += observerB.RecvMagazine;
publisher.PublishMagezine();
//下面的方式会出现编译错误 只允许在定义的内部触发,不允许在外部触发
publisher.Magazine?.Invoke(publisher.magazineName);
Console.ReadKey();
}
}
public class Observer
{
private string name;
public Observer(string name)
{
this.name = name;
}
public void RecvMagazine(string message)
{
Console.WriteLine($"{this.name} recv {message}, 仔细读了一番");
}
}
public class Publisher
{
public string magazineName;
public Publisher(string magazineName)
{
this.magazineName = magazineName;
}
public delegate void MagazineDelegate(string message);
public event MagazineDelegate Magazine;
public void PublishMagezine()
{
Magazine?.Invoke(this.magazineName);
}
}
我们将委托换为事件后发现,不能够直接在外部触发。
publisher.Magazine?.Invoke(publisher.magazineName);
这一句直接报错,编译不通过,即使该事件是public的。
3、标准的事件
class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher("篮球先锋报");
Observer observerA = new Observer("老A");
publisher.Magazine += observerA.RecvMagazine;
Observer observerB = new Observer("老B");
publisher.Magazine += observerB.RecvMagazine;
publisher.PublishMagezine();
Console.ReadKey();
}
}
public class Observer
{
private string name;
public Observer(string name)
{
this.name = name;
}
public void RecvMagazine(object sender,Publisher.MagazineMessageEventArgs e)
{
Console.WriteLine($"{this.name} recv {e.messgae}, 仔细读了一番");
}
}
public class Publisher
{
public class MagazineMessageEventArgs : EventArgs
{
public string messgae;
public MagazineMessageEventArgs(string msg)
{
this.messgae = msg;
}
}
public string magazineName;
public Publisher(string magazineName)
{
this.magazineName = magazineName;
}
public event EventHandler<MagazineMessageEventArgs> Magazine;
public void PublishMagezine()
{
Magazine?.Invoke(this,new MagazineMessageEventArgs(this.magazineName));
}
}
使用了系统自定义的事件及参数类型。其实,很多教程中写道public delegate void EventHandler(object sender, TEventArgs e);中的TEventArgs必须要继承自EventArgs,但是在Framework4.8中我并没有看到那个限定,
所以TEventArgs可以是任何类型,public delegate void EventHandler(object sender, TEventArgs e);就是系统自定义好的一个泛型委托。
我们可以直接将MagazineMessageEventArgs类型改为string类型更加简单。
class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher("篮球先锋报");
Observer observerA = new Observer("老A");
publisher.Magazine += observerA.RecvMagazine;
Observer observerB = new Observer("老B");
publisher.Magazine += observerB.RecvMagazine;
publisher.PublishMagezine();
Console.ReadKey();
}
}
public class Observer
{
private string name;
public Observer(string name)
{
this.name = name;
}
public void RecvMagazine(object sender, string msg)
{
Console.WriteLine($"{this.name} recv {msg}, 仔细读了一番");
}
}
public class Publisher
{
private string magazineName;
public Publisher(string magazineName)
{
this.magazineName = magazineName;
}
public event EventHandler<string> Magazine;
public void PublishMagezine()
{
Magazine?.Invoke(this,this.magazineName);
}
}
执行上述代码和之前的效果一样,其他自定义类应该也是可以的。
4、总结
什么时候使用委托?什么时候使用事件?
如果一个委托不需要再其定义的类之外被触发,那么就可以将其转化为事件,这样可以保证它不会在外部被随意触发。