1. 委托
委托就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法。在IL中委托就是一个类。继承自System.MulticastDelegate 特殊类,不能被继承。
1.1 委托的声明、实例化、调用
1.1.1 声明
委托用deleate关键字修饰,只有方法名。
public delegate string XFInfoDelegate(string name, int age);
1.1.2 实例化、调用
XFInfoDelegate XFInfo = new XFInfoDelegate(GetXFInfo);
XFInfo.Invoke("Olive", 116);
XFInfo(“Olive”,116);//这两种方法都可以调用
实例化的限制就是方法的参数列表&返回值类型必须和委托约束的一致。
1.2 泛型委托
有了泛型委托,就有了一能适用于任何返回类型和任意参数(类型和合理的个数)的通用委托,Func 和 Action(Action Func .NetFramework3.0出现的)。
delegate TResult Func <out TResult> ();
delegate TResult Func <in T, out TResult> (T arg);
delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 arg2);
... 一直到 T16
delegate void Action ();
delegate void Action <in T> (T arg);
delegate void Action <in T1, in T2> (T1 arg1, T2 arg2);
... 一直到 T16
1.2.1 Action
只有输入参数、无返回值。
Action<string, int> SayInfo = (name, age) => Console.WriteLine($"Action Lambda方法:{name} 的年龄是:{age}");//是一种语法糖。
可以直接SayInfo(“XF”,30)调用,
也可以SayInfo.BeginInvoke调用,异步,会在线程池中启动一个线程。
IAsyncResult actionResult = SayInfo.BeginInvoke("XF", 30, (r) => {
if(r==null||r.AsyncState==null)
{
Console.WriteLine("回调异常");
}
Console.WriteLine($"Action 回调成功,结果为:{r.AsyncState}");//可以拿到传入的第三个参数
}, "Action 密码:Olive");
1.2.2 Func
必须有返回值,最有一个参数类型为返回值类型。
Func<string, int, string> PeopleInfo = delegate (string name, int age)
{
ThreadInfo("Func 委托 线程情况打印");
return $"Func采用匿名方法:{name} 的年龄为:{age}";
};
可以直接PeopleInfo (“XF”,30)调用,
也可以PeopleInfo.BeginInvoke调用,异步,会在线程池中启动一个线程。
PeopleInfo.BeginInvoke("XF", 30, (r) => {
Console.WriteLine($"Func 回调成功,结果为:{ PeopleInfo.EndInvoke(r)}");
}, “Func 密码:Olive”);
1.3 委托的意义
1.3.1 解耦
1.3.2 异步多线程
委托可以通过BeginInvoke的方法实现异步多线程。
1.3.3 多播委托
任何一个委托都是多播委托类型的子类,可以通过+=/-=去增加/移除方法,会形成方法链,Invoke时,会按顺序执行系列方法一个委托实例包含多个方法。
1.3.3.1 +=
给委托的实例添加方法,会形成方法链,Invoke时,会按顺序执行系列方法
1.3.3.2 -=
给委托的实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除,且只移除一个,如果没有匹配,则无任何影响。(不同的实例的相同方法不能移除)
多播委托实例不能异步,如果需要异步,可以对委托实例单个方法进行异步调用。
一般情况下多播委托用的是不带返回值的,如果带返回值,则返回最后一个方法的返回值。
1.3.3.3 猫叫示例
1.3.3.3.1 Cat类
猫叫,引发一些列行为
public class Cat
{
/// <summary>
/// 猫叫,并引发一系列的后续行为
/// 类内调用其它类实例方法,依赖多个类型,任何类型变化都需调整
/// </summary>
public void Miao()
{
Console.WriteLine($"{this.GetType().Name} Miao");
new Mouse().Run();
new Dog().Wang();
new Baby().Cry();
Console.WriteLine("该睡觉了");
}
}
1.3.3.3.2 IObject
所有的观察者都需要继承的一个接口
/// <summary>
/// 为所有的观察者定义一个接口
/// </summary>
public interface IObject
{
void DoAction();
}
1.3.3.3.3 Mouse类
/// <summary>
/// 老鼠类,实现了IObject接口,方便后边作为观察者对象被添加
/// </summary>
public class Mouse:IObject
{
public void DoAction()
{
Run();
}
public void Run()
{
Console.WriteLine($"{this.GetType().Name} Run");
}
}
1.3.3.3.4 Dog类
/// <summary>
/// 狗类,实现了IObject接口,方便后边作为观察者对象被添加
/// </summary>
public class Dog:IObject
{
public void DoAction()
{
Wang();
}
public void Wang()
{
Console.WriteLine($"{this.GetType().Name} Wang");
}
}
1.3.3.3.5 Baby类
/// <summary>
/// 儿童类,实现了IObject接口,方便后边作为观察者对象被添加
/// </summary>
public class Baby:IObject
{
public void DoAction()
{
Cry();
}
public void Cry()
{
Console.WriteLine($"{this.GetType().Name} Cry");
}
}
1.3.3.3.6 常规调用
Cat cat = new Cat();
cat.Miao();
1.3.3.3.7 委托实现
现在Cat类中定义一个委托,
public Action CatMiaoAction;
同时定义一个触发委托的方法
public void MiaoDelegate()
{
Console.WriteLine($"{this.GetType().Name} MiaoDelegate");
this.CatMiaoAction?.Invoke();
Console.WriteLine($"{this.GetType().Name} MiaoDelegate OVER");
}
为委托绑定多个方法,
cat.CatMiaoAction += new Mouse().Run;
cat.CatMiaoAction += new Dog().Wang;
cat.CatMiaoAction += new Baby().Cry;
调用
cat.CatMiaoAction.Invoke();
1.4 事件和观察者模式
1.4.1 事件
由5.3.3.7委托实现猫叫示例和5.4.1.2事件实现猫叫示例可以看出,事件和委托的实现基本一样,事件只是多了一个event修饰的委托。实际上相当于委托的一个实例。
事件有一系列规则和约束用以保证程序的安全可控,事件只有 += 和 -= 操作,这样订阅者只能有订阅或取消订阅操作,没有权限执行其它操作。
1.4.1.1 标准事件
创建一个类,在类中声明一个事件
EventHandler(EventHandle是框架封装的委托,该委托有两个参数,一个是object事件源,一个EventArgs类,该类主要用于传递数据),
public class School
{
public event EventHandler SchoolStateHandler;
private string state;
public string State {
get { return state; }
set
{
state = value;
if (value == "OK")//当State改变时,触发绑定事件,并传入State参数
SchoolStateHandler?.Invoke(this, new XFEventArgs() { State = "OK" });
else if (value == "NO")
SchoolStateHandler?.Invoke(this, new XFEventArgs() { State = "NO" });
} }
}
同时创建一个继承于EventArgs的类,用来传递参数
public class XFEventArgs : EventArgs
{
public string State { get; set; }//用来传递State参数
}
创建一些订阅事件的类,其中要有参数为(object obj,EventArgs e)的方法
public class Teacher
{//事件订阅类
public void Teach(object obj,EventArgs e)
{
if (((XFEventArgs)e).State.Equals("OK"))
Console.WriteLine("开始教学");
else if(((XFEventArgs)e).State.Equals("NO"))
Console.WriteLine("停止教学");
}
}
public class Student
{
public void Study(object obj, EventArgs e)
{
if (((XFEventArgs)e).State.Equals("OK"))
Console.WriteLine("开始学习");
else if (((XFEventArgs)e).State.Equals("NO"))
Console.WriteLine("回家休息");
}
}
创建类的实例,并订阅一些方法。
School school = new School();
school.SchoolStateHandler += new Teacher().Teach;
school.SchoolStateHandler += new Student().Study;
当类中某些属性发生变化时触发事件。
school.State = "OK";
school.State = "NO";
结果如下:
1.4.1.2 事件实现5.3.3.3中猫叫示例
1.4.1.2.1 在Cat类中声明一个事件
public event Action CatMiaoCationHandler;(委托是一个类,事件相当于是类的一个实例)
1.4.1.2.2 定义一个执行事件的方法
public void MiaoEvent()
{
Console.WriteLine($"{this.GetType().Name} MiaoEvent");
this.CatMiaoCationHandler?.Invoke();//执行绑定方法
Console.WriteLine($"{this.GetType().Name} MiaoEvent OVER");
}
1.4.1.2.3 为事件绑定一些列方法
cat.CatMiaoCationHandler += new Mouse().Run;
cat.CatMiaoCationHandler += new Dog().Wang;
cat.CatMiaoCationHandler += new Baby().Cry;
1.4.1.2.4 调用
cat.MiaoEvent();
1.4.2 观察者模式
用观察者模式实现5.3.3.3中猫叫示例。
在Cat类中增加观察者集合和添加观察者方法,
通过调用观察者方法,逐一通知观察者。
/// <summary>
/// 添加一个观察者集合,用来存储观察者
/// </summary>
private List<IObject> list = new List<IObject>();
/// <summary>
/// 添加观察者
/// </summary>
/// <param name="observer"></param>
public void AddObserver(IObject observer)
{
list.Add(observer);
}
/// <summary>
/// 猫叫通知观察者
/// </summary>
public void MiaoObserver()
{
foreach(var item in list)
{
item.DoAction();
}
Console.WriteLine($"{this.GetType().Name} Miao Over");
}
调用:
cat.AddObserver(new Mouse());
cat.AddObserver(new Dog());
cat.AddObserver(new Baby());
cat.MiaoObserver();