委托
方法签名
方法签名包括:1.返回类型 2.参数列表 3.方法名称
关键字:Delegate
语法:
//声明
public delegate void GreetingDelegate(string name);
//使用
public void GreetPeople(string name,GreetingDelegate makeGreeting)
{
makeGreeting(name);
}
Delegate
是一个类,在任何可以声明类的地方都可以声明委托
//定义委托,它定义了可以代表的方法的类型
public delegate void GreetingDelegate(string name);
class Program
{
public static void EnglishGreeting(string name)
{
Console.WriteLine($"Morning,{name}");
}
public static void ChineseGreeting(string name)
{
Console.WriteLine($"早上好,{name}");
}
//注意此方法,它接受一个GreetingDelegate类型的方法作为参数
public static void GreetPeople(string name, GreetingDelegate makeGreeting)
{
makeGreeting(name);
}
static void Main(string[] args)
{
GreetPeople("Jimmy Zhang", EnglishGreeting);
GreetPeople("张子阳", ChineseGreeting);
}
}
委托总结
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态的赋给参数的做法,可以避免在程序中大量使用if-else(switch)语句,同时使得程序具有更好的扩展性。
使用变量传递参数
static void Main(string[] args)
{
string name1, name2; //声明
name1 = "Jimmy Funy"; //赋值
name2 = "王小毛"; //赋值
GreetPeople(name1, EnglishGreeting);
GreetPeople(name2, ChineseGreeting);
}
GreetingDelegate delegate1,delegate2; //声明
delegate1 = EnglishGreeting; //赋值
delegate2 = ChineseGreeting; //赋值
GreetPeople("Jimmy Funy", delegate1);
GreetPeople("王小毛", delegate2);
委托可以将多个方法赋给同一个委托,或者说将多个方法绑定到用一个委托,当调用这个委托的时候,将依次调用其所绑定的方法
GreetingDelegate delegate1;
delegate1 = EnglishGreeting;
delegate1 += ChineseGreeting; //绑定第二个方法
GreetPeople("Jimmy Funy", delegate1);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E40E8TTh-1620098690373)(F:\S2\笔记\img\委托.png)]
跳过GreetPeople()
方法,通过委托直接调用EnglishGreeting()
和ChineseGreeting()
并且与之传参
GreetingDelegate delegate1;
delegate1 = EnglishGreeting; //先给委托类型的变量赋值
delegate1 += ChineseGreeting; //给此委托变量再绑定一个方法
//将先后调用EnglishGreeting与ChinaeseGreeting方法
delegate1("Jimmy Funy");
第一次用的“=”是赋值的语法,第二次用的“+=”是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
赋初始值
能使用+=绑定一个方法,同样可以用-=取消一个方法的绑定
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
delegate1 += ChineseGreeting; //给此委托变量再绑定一个方法
//将先后调用EnglishGreeting与ChinaeseGreeting方法
GreetPeople("Jimmy Funy",delegate1);
Console.WriteLine();
delegate1 -= EnglishGreeting; //取消对EnglishGreeting方法的绑定
//将仅调用ChineseGreeting
GreetPeople("王小毛", delegate1);
事件
事件是对委托的封装
语法:访问修饰符 event 委托类型 事件名:
//新建的类
public class GreetingManager
{
//声明事件
public event GreetingDelegate MakeGreet;
public void GreetPeople(string name)
{
MakeGreet(name);
}
通过反编译代码能明确:
MakeGreet
事件确实是一个GreetingDelegate
类型的委托,只不过不管是不是声明为Public,它总是被声明为private。另外,它还有两个方法分别是add_MakeGreee
t和remove_MakeGreet
,两个方法分别对应了“+=“和”-=“,而这两个的访问权限取决于声明事件的访问修饰符
事件限制类型的能力
事件应该由事件发布者触发,而不应该由事件的客户端(客户程序)来触发
//定义委托
public delegate void NumberChangedEventHandler(int count);
//事件发布者类
public class Publishser
{
private int count;
//声明委托变量
// public NumberChangedEventHandler NumberChanged;
//声明一个事件
public event NumberChangedEventHandler NumberChanged;
public void DoSomething()
{
if (NumberChanged != null) //触发事件
{
count++;
NumberChanged(count);
}
}
}
//事件订阅者类
public class Subscriber
{
public void OnNumberChanged(int count)
{
Console.WriteLine($"Subscriber notified:count = {count}");
}
}
static void Main(string[] args)
{
Publishser pub = new Publishser(); //发布者对象
Subscriber sub = new Subscriber(); //订阅者对象
pub.NumberChanged += new NumberChangedEventHandler(sub.OnNumberChanged);
pub.DoSomething(); //应该通过DoSomething()触发事件
//pub.NumberChanged(100); //有委托可以被直接调用,对委托变量使用的不恰当,使用事件后将会报错
}
事件只能为事件发布者在其本身的某个行为中触发,当使用了event关键字之后委托变量成为事件,直接在调用事件的方法是被禁止的
就像我们要定义一个数字类型,我们会用int而不是使用object一样,给予对象过多的能力并不见得是一件好事,应该是越合适越好。
尽管直接使用委托变量通常不会有什么问题,但它给了客户端不应具有的能力,而使用事件,可以限制这一能力,更精准的对类型进行封装。
约定:订阅事件的方法的命名,通常为"On 事件名"比如
OnNumberChanged
Observer设计模式(观察者模式)
设计者模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新
主要包括两类对象:
- Subject:监视对象,它往往包含着其他对象所感兴趣的内容。
- 热水器类就是一个监视对象,当温度快到100时,会不断把数据发给监视它的对象。
- Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采用相应的行动
- 警报器类和显示器类,它们采取的行动分别是发出警报和显示水温
//热水器类
public class Heater
{
private int temperature; //温度
//创建委托
public delegate void BoilHeadler(int param);
//定义事件
public event BoilHeadler 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($"警报:嘀嘀嘀,水已经{param}度了");
}
}
//显示器类
public class DisPlay
{
//显示温度方法
public static void ShowMsg(int param)
{
Console.WriteLine($"提示:水已烧开,当前温度:{param}度");
}
}
事情的发生顺序
- 警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)
- 热水器知道后保留对警报器和显示器的引用
- 热水器进行烧水这一动作,当水温超过95度时,通过对警报器和显示器的引用,自动调用警报器的
MakeAlert()
方法和显示器的ShowMsg()
方法
static void Main(string[] args)
{
Heater heater = new Heater();
Alarm alarm = new Alarm();
heater.BoilEvent += alarm.MakeAlert; //注册方法
heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法
heater.BoilEvent += DisPlay.ShowMsg; //注册静态方法
heater.BoilWater(); //烧水,会自动调用注册过的对象的方法
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-szw8Q1Gc-1620098690378)(F:\S2\笔记\img\Observer设计.png)]
ent += alarm.MakeAlert; //注册方法
heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法
heater.BoilEvent += DisPlay.ShowMsg; //注册静态方法
heater.BoilWater(); //烧水,会自动调用注册过的对象的方法
}
[外链图片转存中...(img-szw8Q1Gc-1620098690378)]