委托本质就是函数的地址,类似c++中的函数指针。
使用委托,可以实现把函数作为参数,在另一个函数中调用。
使用委托可以将函数引用封装在委托对象内。然后调用该委托对象就可以执行委托对象内函数引用指向的函数,而不必在编译时知道将调用哪个函数。
多播委托(委托链):通过+=不断的进行注册方法,实现一次性调用多个方法。
好处是:
class UseDelegate
{
//2.声明委托
InformInfoHandler myInform;
public UseDelegate()
{
//3.委托注册
myInform = new InformInfoHandler(InformA);
myInform += new InformInfoHandler(InformB);
}
public void SendInfo()
{
//4.调用委托(直接调用)
myInform("I am cx");
}
private void InformA(string name)
{
Console.WriteLine("{0},Hello A", name);
}
private void InformB(string name)
{
Console.WriteLine("{0},Hello B", name);
}
}
委托本质上就是一个“类”,能定义类的地方,都可以定义委托。
(可通过反编译软件,进行查看证明,委托就是继承了[mscorlib]System.MulticastDelegate的特殊类)
三个核心方法:BeginInvoke,EndInvoke和Invoke
封送一个委托方法,类似于使用PostMessage进行通信,异步操作,使用EndInvoke方法等待异步操作的完成。
就是一个能存放很多函数指针的调用列表(但函数签名必须和委托类型签名一样),你一调用这个列表,那么列表里的所有的指针所对应的方法依次被执行。
委托对象 obj 在创建时创建了指向方法 SayHi的指针并保存在 _methodPtr中;_target中保存了SayHi方法所在的类的对象(比如我把这段代码写在窗体里按钮的点击方法中,那么此时 _target就是 SayHi方法所在的窗体对象);_invocationList 中保存了追加的两个方法的指针。
函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。
在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。
其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。
事件是基于特定类的,用于事件驱动模型的专用委托。
事件本质是对委托的封装,对外提供add_EventName(对应+=)和remove_EventName(对应-=)访问
可以将公有的委托变量定义为私有变量,从而满足类的封装性原则
Event就是封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。
声明一个事件
public event InformInfoHandler eveMyInform;
事件本质上是委托的一个实例
委托可以定义在类的外部,事件只能定义在类的内部,封装性。
委托你不但可以安排谁是它的调用函数,还可以直接调用它,而事件不能直接调用,只能通过某些操作触发。事件仅仅是供其他类型订阅,而客户端不能直接触发事件。
public class EventTest
{
//1.定义delegate对象
public delegate void MyEventHandler(object sender, System.EventArgs e);
//2.(定义事件参数类)省略
public class MyEventCls
{
//3.定义事件处理方法,它与delegate对象具有相同的参数和返回值类型
public void MyEventFunc(object sender, System.EventArgs e)
{
Console.WriteLine("My event is ok!");
}
}
//4.用event关键字定义事件对象
private event MyEventHandler myevent;
private MyEventCls myecls;
public EventTest()
{
myecls = new MyEventCls();
//5.用+=操作符将事件添加到队列中
this.myevent += new MyEventHandler(myecls.MyEventFunc);
}
//6.以调用delegate的方式写事件触发函数
protected void OnMyEvent(System.EventArgs e)
{
if (myevent != null)
myevent(this, e);
}
public void RaiseEvent()
{
EventArgs e = new EventArgs();
//7.触发事件
OnMyEvent(e);
}
public static void Main()
{
EventTest et = new EventTest();
et.RaiseEvent();
}
}
假设我们有个高档的热水器,我们给它通上电,当水温超过95度的时候:1、扬声器会开始发出语音,告诉你水的温度;2、液晶屏也会改变水温的显示,来提示水已经快烧开了。
热水器由三部分组成:热水器、警报器、显示器。热水器仅仅负责烧水,它不能发出警报也不能显示水温;在水烧开时由警报器发出警报、显示器显示提示和水温。
namespace Delegate
{
// 热水器
public class Heater
{
private int temperature;
public string type = "RealFire 001"; // 添加型号作为演示
public string area = "China Xian"; // 添加产地作为演示
//声明委托
public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);
public event BoiledEventHandler Boiled; //声明事件
// 定义BoiledEventArgs类,传递给Observer所感兴趣的信息
public class BoiledEventArgs : EventArgs
{
public readonly int temperature;
public BoiledEventArgs(int temperature)
{
this.temperature = temperature;
}
}
// 可以供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视
protected virtual void OnBoiled(BoiledEventArgs e)
{
if (Boiled != null)
{ // 如果有对象注册
Boiled(this, e); // 调用所有注册对象的方法
}
}
// 烧水
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature > 95)
{
//建立BoiledEventArgs 对象
BoiledEventArgs e = new BoiledEventArgs(temperature);
OnBoiled(e); // 调用 OnBolied方法
}
}
}
}
// 警报器
public class Alarm
{
public void MakeAlert(Object sender, Heater.BoiledEventArgs e)
{
Heater heater = (Heater)sender; //这里是不是很熟悉呢?
//访问 sender 中的公共字段
Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);
Console.WriteLine("Alarm: 嘀嘀嘀,水已经 {0} 度了:", e.temperature);
Console.WriteLine();
}
}
// 显示器
public class Display
{
public static void ShowMsg(Object sender, Heater.BoiledEventArgs e)
{ //静态方法
Heater heater = (Heater)sender;
Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);
Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", e.temperature);
Console.WriteLine();
}
}
class Program
{
static void Main()
{
Heater heater = new Heater();
Alarm alarm = new Alarm();
heater.Boiled += alarm.MakeAlert; //注册方法
heater.Boiled += (new Alarm()).MakeAlert; //给匿名对象注册方法
heater.Boiled += newHeater.BoiledEventHandler(alarm.MakeAlert); //也可以这么注册
heater.Boiled += Display.ShowMsg; //注册静态方法
heater.BoilWater(); //烧水,会自动调用注册过对象的方法
}
}
}
方法一:将事件声明为private的,然后提供两个方法来进行注册和取消注册
public class Publishser
{
// 声明一个私有事件
private event GeneralEventHandler NumberChanged;//将NumberChanged声明为委托变量还是事件都无所谓了,因为它是私有的
// 注册事件
public void Register(GeneralEventHandler method)
{
NumberChanged = method;//“=”,而非“+=”,通过这种方式就避免了多个方法注册
}
// 取消注册
public void UnRegister(GeneralEventHandler method)
{
NumberChanged -= method;//即使method方法没有进行过注册,也不会抛出异常,仅仅是不会产生任何效果而已。
}
public void DoSomething()
{
if (NumberChanged != null)
{
// 触发事件
string rtn = NumberChanged();
Console.WriteLine("Return: {0}", rtn);
}
}
}
方法二:C#中提供了一种叫事件访问器(Event Accessor)的东西,它用来封装委托变量。
class Program
{
static void Main(string[] args)
{
Publishser pub = new Publishser();
Subscriber1 sub1 = new Subscriber1();
Subscriber2 sub2 = new Subscriber2();
pub.NumberChanged -= sub1.OnNumberChanged; // 不会有任何反应
pub.NumberChanged += sub2.OnNumberChanged; // 注册了sub2
pub.NumberChanged += sub1.OnNumberChanged; // sub1将sub2的覆盖掉了
pub.DoSomething();// 触发事件
}
}
// 定义委托
public delegate string GeneralEventHandler();
// 定义事件发布者
public class Publishser
{
// 声明一个委托变量
private GeneralEventHandler numberChanged;
// 事件访问器的定义
public event GeneralEventHandler NumberChanged
{
add
{
numberChanged = value;
}
remove
{
numberChanged -= value;
}
}
public void DoSomething()
{
if (numberChanged != null)
{
numberChanged();// 通过委托变量触发事件
}
}
}
// 定义事件订阅者
public class Subscriber1
{
public string OnNumberChanged()
{
Console.WriteLine("Subscriber1 Invoked!");
return "Subscriber1";
}
}
public class Subscriber2 {/* 与上类同,略 */}
ref:
https://www.cnblogs.com/sjqq/p/6917497.html