新加的理解:
https://blog.youkuaiyun.com/lizhenxiqnmlgb/article/details/82141968 还不错。
回顾张子阳的博客关于事委与托件的总结
1.关于方法作为方法的参数传递
先看一个参数传递的方式
public enum Language{
English,Chinese
}
public void SayHai(string name,Language lang)
{
swich(lang)
case Language.English:
EnglishGreeting(name);
break;
case Language.CHiniese:
ChineseGreeting(name);
break;
}
这样的问题是日文韩文来了,改动就大了。
怎么办,C#不同于C的方式是可以传入类型作为参数
声明的时候关键词Delegate就是类,声明出来的委托也就可以理解为类型了,
Public void f1(string name,B b);
上面的代码,你想一想,你会给第一个参数string传一个1过去吗?编译器不认,同理委托的B所有格式也应该一样(包含方法参数和方法返回类型)!
我们在看看委托定义的格式
public delegate void GreetingDelegate(string name);
再看看具体应用
namespace Delegate{
public delegate void GreetingDelegate(string name);//首先确认了使用范围只有在Delegate里面使用
prevate static void GreetingPeople(string name,GreetingDelegate MakeGreeting )
{
MakeGreeting(name);
}
//省略中国人说法
//外国人说法
//省略主体程序Main开头
GreetingPeople("威威",CinenseGreeting)//使用了传递类型的方法
]
总结一下 委托 就是类型参数传递方法的有效手段。
2.类型与引用关系
为什么提这个看看下面的例子结合类型与引用就知道了
static void Main(string[] args) {
GreetingDelegate delegate1, delegate2;
delegate1 = EnglishGreeting;
delegate2 = ChineseGreeting;
GreetPeople("Jimmy Zhang", delegate1);
GreetPeople("张子阳", delegate2);
Console.ReadKey();
}
EnglishGreeting和ChineseGreeting方法名(当做C的指针,本来C#底层就是在处理指针地址),把地址赋给delegate1 ,delegate2,实际上撒都没有做,就是多了个引用,放心大胆的使用 delegate1 ,2,来当作原方法使用。
多个方法绑定一个委托
static void Main(string[] args) {
GreetingDelegate delegate1;
delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
GreetPeople("Jimmy Zhang", delegate1);
Console.ReadKey();
}
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
还有一种方式比较生疏但用引用指针的原则很好理解
static void Main(string[] args){
GreetingDelegate delegate1;
delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
delegate1("weiwei");//这个就看出来了,并没有当作类型参数传递,
但是可以当作函数指针指向EnglishGreeting,ChineseGreeting。
}
注意两个误区
我们也可以使用下面的代码来这样简化这一过程:
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
长得太特么想引用与实例化了,实际上就是构造函数中传入的一个方法指针地址。
用例外个Demo也能证明这一点
GreetingDelegate delegate1 = new GreetingDelegate();
delegate1 += EnglishGreeting; // 这次用的是 “+=”,绑定语法。
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方
这个地方报““GreetingDelegate”方法没有采用“0”个参数的重载”错误。
知道了+=,那么-=更好理解。
事件
为了对象封装性,delegate1不能随意被人更改, Event出场了,它封装了委托类型的变量
事件其实没什么不好理解的,声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。
下面的Demo是Observer设计模式,把三个完全不同的类对象联系在一起,一个对象有变化,会通知其他种类的对象,其他对象就可以相应动作了。
using System;
using System.Collections.Generic;
using System.Text;
namespace Delegate {
// 热水器
public class Heater {
private int temperature;
public delegate void BoilHandler(int param); //声明委托
public event BoilHandler BoilEvent; //声明事件
// 烧水
public void BoilWater() {
for (int i = 0; i <= 100; i++) {
temperature = i;
if (temperature > 95) {
if (BoilEvent != null) { //如果有对象注册
BoilEvent(temperature); //调用所有注册对象的方法
}
}
}
}
}
// 警报器
public class Alarm {
public void MakeAlert(int param) {
Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);
}
}
// 显示器
public class Display {
public static void ShowMsg(int param) { //静态方法
Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);
}
}
class Program {
static void Main() {
Heater heater = new Heater();
Alarm alarm = new Alarm();
heater.BoilEvent += alarm.MakeAlert; //注册方法
heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法 @@@@@@@@@@@@很奇怪的绑定方式,就把他当作另外一个对象,因为只注册了一次而且有没有名称,就叫匿名对象好了
heater.BoilEvent += Display.ShowMsg; //注册静态方法
heater.BoilWater(); //烧水,会自动调用注册过对象的方法
}
}
}
输出为:
Alarm:嘀嘀嘀,水已经 96 度了:
Alarm:嘀嘀嘀,水已经 96 度了:
Display:水快烧开了,当前温度:96度。
// 省略...