一、啥是委托
1.委托和方法是类似的,有参数、有返回值、有修饰符、+delegate。
2.在反编译中,我们所定义的委托在CustomDelegate中都有对应的calss,所以委托的本质:委托类----继承自System.MulticastDelegate的类(MulticastDelegate是一个继承自delegate的特殊的抽象类,自定义类的时候无法主动继承该类,MulticastDelegate类的内部包含有构造函数+方法)
我自己的感受:委托就像是一个自动贩卖机,方法是里面的商品,方法名是商品对应的序号,只要你输入对了序号就能定位到正确的商品。
二、委托实例化与使用
委托可以通过new来实例化使用,要求必须传递一个和这个委托的参数和返回值完全匹配的方法
在Invoke()时需要根据委托的定义来进行传参
下面例子使用到了委托类:
/// <summary>
/// 1.无参数无返回值委托
/// </summary>
public delegate void NoReturnNoParameter1();
public class DelegateClass
{
/// <summary>
/// 2.无参数无返回值委托
/// </summary>
public delegate void NoReturnNoParameter2();
/// <summary>
/// 3.有参数无返回值委托
/// </summary>
public delegate void NoReturn(int x, int y);
/// <summary>
/// 4.无参数有返回值委托
/// </summary>
/// <returns></returns>
public delegate int NoParameter();
/// <summary>
/// 5.有参数有返回值委托
/// </summary>
/// <returns></returns>
public delegate int ReturnAndParameter(out int x, ref int y);
/// <summary>
/// 1.2无参数无返回值的方法
/// </summary>
public void NoReturnNoParameterMethod()
{
Console.WriteLine("这是一个无参数无返回值的方法");
}
/// <summary>
/// 3.有参数无返回值的方法
/// </summary>
public void NoReturnMethod(int x, int y)
{
Console.WriteLine("这是一个有参数无返回值的方法:x={0},y={1}", x, y);
}
/// <summary>
/// 4.无参数有返回值的方法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public int NoParameterMethod()
{
Console.WriteLine("这是一个无参数有返回值的方法");
return 1;
}
/// <summary>
/// 5.有参数有返回值的方法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int ReturnAndParameterMethod(out int x, ref int y)
{
x = 1;
Console.WriteLine("这是一个有参数有返回值的方法:x={0},y={1}", x, y);
return y;
}
}
复制代码
在上面的委托类中增加一个示例方法Show():
/// <summary>
/// 委托实例化使用示例
/// 上面的代码中,我们定义了一个委托类型的变量,并且将一个方法作为委托的实例化,
/// 委托的有无参数、返回值,要与方法的有无参数、返回值保持一致。
/// </summary>
public void Show()
{
//===================================多种实例化==================================================
//第一种方法(委托实例化后传入返回值、参数完全相同的方法的方法名)
NoReturnNoParameter1 delegate1 = new NoReturnNoParameter1(NoReturnNoParameterMethod);
NoReturnNoParameter2 delegate2 = new NoReturnNoParameter2(NoReturnNoParameterMethod);
NoReturn delegate3 = new NoReturn(NoReturnMethod);
NoParameter delegate4 = new NoParameter(NoParameterMethod);
ReturnAndParameter deletegate5 = new ReturnAndParameter(ReturnAndParameterMethod);
//第二种方法(直接指向返回值、参数完全相同的方法)----编译器提供的语法糖
NoReturnNoParameter1 delegate11 = NoReturnNoParameterMethod;
NoReturnNoParameter2 delegate22 = NoReturnNoParameterMethod;
NoReturn delegate33 = NoReturnMethod;
NoParameter delegate44 = NoParameterMethod;
ReturnAndParameter deletegate55 = ReturnAndParameterMethod;
//第三种方式(直接指向Lambda表达式)
NoReturnNoParameter1 delegate111 = () => { };
NoReturnNoParameter2 delegate222 = () => { };
NoReturn delegate333 = ( x, y ) => { };
NoParameter delegate444 = () => 1;
ReturnAndParameter deletegate555 = (out int x, ref int y) => { x = 1; y = 2; return y; };
//===================================执行委托的三个方法=======================================
delegate1.Invoke();
delegate2.Invoke();
delegate3.Invoke(1, 2);
int num1 = delegate4.Invoke();
int y = 2;
int num2= deletegate5.Invoke(out num2, ref y);
//开启线程执行委托
//delegate1.BeginInvoke(null, null);
//回调
//delegate1.EndInvoke(null);
}
复制代码
调用方法测试:
static void Main(string[] args)
{
DelegateClass delegateClass = new DelegateClass();
delegateClass.Show();
}
复制代码
执行结果:
三、委托的作用与意义
委托的优势:
1.代码更稳定,中控方法不需要修改
2.多个情况需要的执行逻辑不在同一个方法中,减少了局部改动测试整体的需要。--逻辑解耦
3.从上而下减少了重复代码,公共逻辑宠用
使用委托的场景建议:
1.方法内部业务逻辑耦合严重
2.如果多个方法,有很多重复代码---去掉重复代码--逻辑重用
需求场景:需要满足多条件多方案并且后续会有公共业务逻辑增加
比如:中国人讲中文,美国人讲英语,日本人讲日语
后期需求:
增加公共逻辑:说话前要举手
正常做法会是:
1.在同一个方法中根据传递进来的标识符进行判断
----缺点:如果变更需求就需要更改原方法,可能会导致整个方法进行重新测试。
----优点:增加公共逻辑方便
2.写多个方法,然后按需调用
----优点:相比第一个方法,只需要新增一个独立的方法,对其他的功能不会造成影响
----缺点:如果需要增加公共逻辑需要每个方法都进行重复代码的新增
上面两个方法优缺点都很明显,委托可以完美的将上面两个方案的优点结合避免缺点出现(既没有大量重复代码,也相对稳定),便于后期维护。
代码:
public class PeopleClass
{
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 国家
/// </summary>
public string Country { get; set; }
/// <summary>
/// 中国的人语言
/// </summary>
public void Chinese()
{
Console.WriteLine("说中文");
}
/// <summary>
/// 美国人的语言
/// </summary>
public void Americans()
{
Console.WriteLine("Say English");
}
/// <summary>
/// 日本人的语言
/// </summary>
public void Japanese()
{
Console.WriteLine("は日本語を話す");
}
//=====================================================这是增加公共逻辑前后的分割线======================================================================
/// <summary>
/// 定义一个委托
/// </summary>
public delegate void PeopleLanguage();
/// <summary>
/// 声明一个传递委托的方法,方便共用新增的通用逻辑
/// </summary>
/// <param name="language"></param>
public void PeopleMethod(PeopleLanguage language)
{
//新增的公共逻辑:说话前要举手
Console.WriteLine("举手!");
language.Invoke();
}
}
复制代码
调用方法:
static void Main(string[] args)
{
PeopleClass peopleClass = new PeopleClass();
//中国人说话
PeopleLanguage peopleLanguage = new PeopleLanguage(peopleClass.Chinese);
peopleClass.PeopleMethod(peopleLanguage);
//美国人说话
PeopleLanguage peopleLanguage2 = new PeopleLanguage(peopleClass.Americans);
peopleClass.PeopleMethod(peopleLanguage2);
//日本人说话
PeopleLanguage peopleLanguage3 = new PeopleLanguage(peopleClass.Japanese);
peopleClass.PeopleMethod(peopleLanguage3);
}
复制代码
结果:
四、俄罗斯套娃----ASP.NET CORE的核心思想(委托的嵌套使用)
把委托这个箱子,一层一层的包包装,在每一次执行的时候,增加一些业务逻辑.要执行的核心业务逻辑是在最内部的这个箱子中,执行顺序是从最外层执行到最内层一层一层的执行出来。
引用场景:AOP;装饰器
委托嵌套Demo(1)----简易版:
示意图:
public class DelegateNestedClass
{
//第一步 声明一个委托
public delegate void MyNameIsDelegate();
public static void ThisIsClass()
{
//第二步 实例化类
MyNameIsClass myNameIsClass = new MyNameIsClass();
//第三步 通过反射获取类中的方法
Type type = myNameIsClass.GetType();
MethodInfo methodInfo = type.GetMethod("MyNameIsMethod");
//第四步 实例化委托→委托中存在行为→行为执行方法
MyNameIsDelegate myNameIsDelegate = new MyNameIsDelegate(() => {
methodInfo.Invoke(myNameIsClass, null);
});
//第五步 套娃开始
//1.第一层套娃
MyNameIsDelegate myNameIsDelegate1 = new MyNameIsDelegate(() => {
myNameIsDelegate.Invoke();
});
//2.第二层套娃
MyNameIsDelegate myNameIsDelegate2 = new MyNameIsDelegate(() => {
myNameIsDelegate1.Invoke();
});
//第六步 执行委托
myNameIsDelegate2.Invoke();
}
}
/// <summary>
/// 我是一个类
/// </summary>
public class MyNameIsClass
{
/// <summary>
/// 我是一个方法
/// </summary>
public void MyNameIsMethod()
{
Console.WriteLine("我是一个方法");
}
}
复制代码
委托嵌套Demo(2)----进阶版(特性):
可以自由的在执行Method方法的前后增加业务逻辑,如下图所示:
public delegate void MyNameIsDelegate();
public static void ThisIsClass()
{
MyNameIsClass myNameIsClass = new MyNameIsClass();
Type type = myNameIsClass.GetType();
MethodInfo methodInfo = type.GetMethod("MyNameIsMethod");
MyNameIsDelegate myDelegate= new MyNameIsDelegate(() =>
{
methodInfo.Invoke(myNameIsClass, null);
});
if (methodInfo.IsDefined(typeof(MethodAttribute), true))
{
foreach (MethodAttribute methodAttribute in methodInfo.GetCustomAttributes())
{
myDelegate = methodAttribute.Do(myDelegate);
}
}
myDelegate.Invoke();
}
public abstract class MethodAttribute: Attribute
{
public abstract MyNameIsDelegate Do(MyNameIsDelegate myNameIsDelegate);
}
/// <summary>
/// 定义一个特性,特性中存在一个以委托为方法
/// </summary>
public class LogAttribute : MethodAttribute
{
public override MyNameIsDelegate Do(MyNameIsDelegate myNameIsDelegate)
{
MyNameIsDelegate myDelegate=new MyNameIsDelegate(()=>{
Console.WriteLine("LogAttribute在执行委托之前输出了一条日志");
myNameIsDelegate.Invoke();
Console.WriteLine("LogAttribute在执行委托之后输出了一条日志");
});
return myDelegate;
}
}
/// <summary>
/// 又定义一个特性,特性中存在一个以委托为方法
/// </summary>
public class LogAgainAttribute : MethodAttribute
{
public override MyNameIsDelegate Do(MyNameIsDelegate myNameIsDelegate)
{
MyNameIsDelegate myDelegate = new MyNameIsDelegate(() => {
Console.WriteLine("LogAgainAttribute在执行委托之前输出了一条日志");
myNameIsDelegate.Invoke();
Console.WriteLine("LogAgainAttribute在执行委托之后输出了一条日志");
});
return myDelegate;
}
}
复制代码
使用到的类:
/// <summary>
/// 我是一个类
/// </summary>
public class MyNameIsClass
{
/// <summary>
/// 我是一个方法
/// </summary>
[Log]
[LogAgain]
public void MyNameIsMethod()
{
Console.WriteLine("我是一个方法");
}
}
复制代码
执行结果:
五、框架内置委托Action/Func----建议使用委托的时候直接使用Action/Func
Action:来自于System.RunTime的一个声明好无返回值(可带有参数)的delegate
Func来自于System.RunTime的一个声明好有返回值(可带有参数)的delegate
----尖括号中前面类型参数:输入参数(最多支持16个),最后面的类型参数是作为返回值
Action和Func存在的意义:
委托的本质就是类,定义多个委托,其实就是新增了多个类。使用Action/Func便不需要我们每次都去新增委托,可以把委托做到统一。
/// <summary>
/// 1.无参数无返回值的方法
/// </summary>
public void NoReturnNoParameterMethod()
{
Console.WriteLine("这是一个无参数无返回值的方法");
}
/// <summary>
/// 2.有参数无返回值的方法
/// </summary>
public void NoReturnMethod(int x, int y)
{
Console.WriteLine("这是一个有参数无返回值的方法:x={0},y={1}", x, y);
}
/// <summary>
/// 3.无参数有返回值的方法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public int NoParameterMethod()
{
Console.WriteLine("这是一个无参数有返回值的方法");
return 1;
}
/// <summary>
/// 4.有参数有返回值的方法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int ReturnAndParameterMethod(int x, string y)
{
Console.WriteLine("这是一个有参数有返回值的方法:x={0},y={1}", x, y);
return x;
}
public void show()
{
//无返回值无参数Action示例
Action action = new Action(NoReturnNoParameterMethod);
action.Invoke();
//无返回值有参数Action示例
Action<int, int> action2 = new Action<int, int>(NoReturnMethod);
action2.Invoke(1, 2);
//有返回值无参数Action示例
Func<int> func = new Func<int>(NoParameterMethod);
func.Invoke();
//有返回值有参数Action示例
Func<int, string, int> func2 = new Func<int, string, int>(ReturnAndParameterMethod);
func2.Invoke(4, "Hello Word");
}
复制代码
打破最多入参16个的限制(虽然应该是用不到的):
public class FrameWorkDelegate
{
public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, in T17,in T18>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17,T18 arg18);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, in T17, in T18,out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18);
public void show()
{
//传入18个参数
Action<int, string, int, string, int, string, int, string, int, string, int, string, int, string, int, string, int, int> action = null;
//传入18个参数
Func<int, string, int, string, int, string, int, string, int, string, int, string, int, string, int, string, int, int, int> func = null;
}
}
复制代码
六、多播委托
可以通过+= 把多个方法添加到这个委托中去,执行委托的时候,按照添加方法的顺序依次去执行委托。 可以通过-=移除方法---是从后往前,逐个匹配,如果匹配不到,就不做任何操作就不做任何操作;如果匹配到,就把当前这个移除,且停止去继续往后匹配。
----实例中的方法无法移除,比如示例中的:new Student().NoReturnNoParameterMethodThree,因为此处相当于每次都声明一个新的实例,所以此处与上面的并不是同一个实例导致移除无效。
----lambda表达式也无法移除,因为lambada表达式是在底层会生成一个方法。
1.增加方法(+=)
额外用到的类:
public class Student
{
public void NoReturnNoParameterMethodThree()
{
Console.WriteLine("第三个执行的方法");
}
public static void NoReturnNoParameterMethodFour()
{
Console.WriteLine("第四个执行的方法");
}
public void NoReturnMethodThree(int x, string y)
{
Console.WriteLine($"第三个执行的方法:x={x},y={y}");
}
public static void NoReturnMethodFour(int x, string y)
{
Console.WriteLine($"第四个执行的方法:x={x},y={y}");
}
public int NoParameterMethodThree()
{
Console.WriteLine("第三个执行的方法");
return 1;
}
public static int NoParameterMethodFour()
{
Console.WriteLine("第四个执行的方法");
return 1;
}
public int ReturnAndParameterMethodThree(int x, string y)
{
Console.WriteLine("第三个执行的有参数有返回值的方法:x={0},y={1}", x, y);
return x;
}
public static int ReturnAndParameterMethodFour(int x, string y)
{
Console.WriteLine("第四个执行的有参数有返回值的方法:x={0},y={1}", x, y);
return x;
}
}
复制代码
示例类:
public class FrameWorkDelegate
{
/// <summary>
/// 1.无参数无返回值的方法
/// </summary>
public void NoReturnNoParameterMethod()
{
Console.WriteLine("第一、五次执行的无参数无返回值的方法");
}
public void NoReturnNoParameterMethod2()
{
Console.WriteLine("第二次执行的无参数无返回值的方法");
}
/// <summary>
/// 2.有参数无返回值的方法
/// </summary>
public void NoReturnMethod(int x, string y)
{
Console.WriteLine($"第一、五次执行的参数无返回值的方法:x={x},y={y}");
}
public void NoReturnMethod2(int x, string y)
{
Console.WriteLine($"第二次执行的有参数无返回值的方法:x={x},y={y}");
}
/// <summary>
/// 3.无参数有返回值的方法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public int NoParameterMethod()
{
Console.WriteLine("第一、五次执行的无参数有返回值的方法");
return 1;
}
public int NoParameterMethod2()
{
Console.WriteLine("第二次执行的无参数有返回值的方法");
return 1;
}
/// <summary>
/// 4.有参数有返回值的方法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int ReturnAndParameterMethod(int x, string y)
{
Console.WriteLine("第一、五次执行的有参数有返回值的方法:x={0},y={1}", x, y);
return x;
}
public int ReturnAndParameterMethod2(int x, string y)
{
Console.WriteLine("第二次执行的有参数有返回值的方法:x={0},y={1}", x, y);
return x;
}
public void show()
{
Console.WriteLine("============无参无返回值示例============");
Action action1 = new Action(NoReturnNoParameterMethod);
action1 += NoReturnNoParameterMethod2;
//Student类中的一个无参无返回值的方法
action1 += new Student().NoReturnNoParameterMethodThree;
//Student类中的一个无参无返回值的静态方法
action1 += Student.NoReturnNoParameterMethodFour;
//可重复添加同一个方法
action1 += NoReturnNoParameterMethod;
action1 += () =>
{
Console.WriteLine("这是lambda表达式");
};
action1.Invoke();
//多播委托开启线程执行
foreach (Action action in action1.GetInvocationList())
{
//不知道是不是.net版本升级的问题,BeginInvoke使用一直报错,但是一直到不到报错原因,只能换位Task.Run()
//action.BeginInvoke(null, null);
Task.Run(() => action.Invoke());
}
Console.WriteLine("============有参无返回值示例============");
Action<int, string> action2 = new Action<int, string>(NoReturnMethod);
action2 += NoReturnMethod2;
action2 += new Student().NoReturnMethodThree;
action2 += Student.NoReturnMethodFour;
action2 += NoReturnMethod;
action2 += (x, y) =>
{
Console.WriteLine($"这是lambda表达式{x},{y}");
};
action2.Invoke(1, "2");
//多播委托开启线程执行
foreach (Action<int, string> action in action2.GetInvocationList())
{
//action.BeginInvoke(1,"2",null, null);
Task.Run(() => action.Invoke(1, "2"));
}
Console.WriteLine("============无参有返回值示例============");
Func<int> func1 = new Func<int>(NoParameterMethod);
func1 += NoParameterMethod2;
func1 += new Student().NoParameterMethodThree;
func1 += Student.NoParameterMethodFour;
func1 += NoParameterMethod;
func1 += () =>
{
Console.WriteLine("这是lambda表达式");
return 1;
};
func1.Invoke();
//多播委托开启线程执行
foreach (Func<int> func in func1.GetInvocationList())
{
//func.BeginInvoke(null, null);
Task.Run(() => func.Invoke());
}
Console.WriteLine("============有参有返回值示例============");
Func<int, string, int> func2 = new Func<int, string, int>(ReturnAndParameterMethod);
func2 += ReturnAndParameterMethod2;
func2 += new Student().ReturnAndParameterMethodThree;
func2 += Student.ReturnAndParameterMethodFour;
func2 += ReturnAndParameterMethod;
func2 += (x, y) =>
{
Console.WriteLine($"这是lambda表达式{x},{y}");
return 1;
};
func2.Invoke(3, "4");
foreach (Func<int, string, int> func in func2.GetInvocationList())
{
//func.BeginInvoke(3,"4",null,null);
Task.Run(() => func.Invoke(3,"4"));
}
}
}
复制代码
调用:
static void Main(string[] args)
{
FrameWorkDelegate frameWorkDelegate = new FrameWorkDelegate();
frameWorkDelegate.show();
}
复制代码
执行结果:
2.增加方法(-=)
示例:
public void show()
{
Console.WriteLine("============无参无返回值示例============");
Action action1 = new Action(NoReturnNoParameterMethod);
action1 += NoReturnNoParameterMethod2;
//Student类中的一个无参无返回值的方法
action1 += new Student().NoReturnNoParameterMethodThree;
//Student类中的一个无参无返回值的静态方法
action1 += Student.NoReturnNoParameterMethodFour;
//可重复添加同一个方法
action1 += NoReturnNoParameterMethod;
action1 += () =>
{
Console.WriteLine("这是lambda表达式");
};
//移除方法
action1 -= NoReturnNoParameterMethod;
action1 -= NoReturnNoParameterMethod2;
//此处移除无效,因为new Student()相当于每次都声明一个新的实例,所以此处与上面的并不是同一个实例导致移除无效。lambda表达式也无法移除,因为lambada表达式是在底层会生成一个方法。
action1 -= new Student().NoReturnNoParameterMethodThree;
action1 -= Student.NoReturnNoParameterMethodFour;
action1.Invoke();
}
复制代码
结果:
七、事件和委托
示例背景:猫叫了→老鼠跑了→狗叫了→小孩哭了(猫叫了一声引发了一系列反应)
解决方案:
1.在猫叫的方法中按照顺序调用方法
----依赖太重、职责不单一
public class Cat
{
/// <summary>
/// 猫叫了
/// </summary>
public void Meow()
{
Console.WriteLine("猫叫了");
#region 普通的方法
new Mouse().Run();
new Dog().Bark();
new Baby().Cry();
#endregion
}
}
public class Mouse
{
/// <summary>
/// 老鼠跑了
/// </summary>
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
public class Dog
{
/// <summary>
/// 狗叫了
/// </summary>
public void Bark()
{
Console.WriteLine("狗叫了");
}
}
public class Baby
{
/// <summary>
/// 小孩哭了
/// </summary>
public void Cry()
{
Console.WriteLine("小孩哭了");
}
}
复制代码
2.观察者模式(将不属于我的东西转移出去)
①.多播委托----从语法层面支持
此处有两个几乎一模一样的例子:
例子a是委托
例子b是事件
关系:事件是委托的实例(实例是特殊的委托)。
区别:事件比委托更安全
①.事件只能在所在类的内部执行(比如:子类无法使用父类中的事件,但是可以使用父类中的委托。实现类中无法调用目标类中的事件,但是可以调用目标类中的委托)
②.委托可以直接赋值null进行清空,事件不可以。
事件应用场景:当需求固定要在某一个动作之后再按照顺序执行一些方法的时候,使用事件防止别人跳过动作调执行方法。
a.多播委托
----将所有的方法通过多播委托+=到action中,使每个方法的职责单一,保证了所有方法的稳定性
使用到的类:
public class Cat
{
/// <summary>
/// 猫叫了
/// </summary>
public void Meow()
{
Console.WriteLine("猫叫了");
}
#region 多播委托用到的
public Action catAction = null;
public void CatActionMethod()
{
Console.WriteLine("开始蝴蝶效应了");
if (catAction!=null)
{
catAction.Invoke();
}
}
#endregion
}
public class Mouse
{
/// <summary>
/// 老鼠跑了
/// </summary>
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
public class Dog
{
/// <summary>
/// 狗叫了
/// </summary>
public void Bark()
{
Console.WriteLine("狗叫了");
}
}
public class Baby
{
/// <summary>
/// 小孩哭了
/// </summary>
public void Cry()
{
Console.WriteLine("小孩哭了");
}
}
复制代码
使用多播委托进行调用:
static void Main(string[] args)
{
Cat cat = new Cat();
#region 多播委托
cat.catAction += cat.Meow;
cat.catAction += new Mouse().Run;
cat.catAction += new Dog().Bark;
cat.catAction += new Baby().Cry;
cat.CatActionMethod();
#endregion
}
复制代码
执行结果:
b.事件
事件观察者模式的三大要素:
1.发布者
2.订阅者
3.订阅。
多在winform中的控件事件绑定使用,ASP.NET MV5的管道处理模型就是通过十九大事件完成的。
基于a的实例增加一个Event关键字
用到的类:
public class Cat
{
/// <summary>
/// 猫叫了
/// </summary>
public void Meow()
{
Console.WriteLine("猫叫了");
}
#region 事件用到的
public event Action catEventAction = null;
public void CatEventActionMethod()
{
if (catEventAction != null)
{
Console.WriteLine("开始蝴蝶效应了");
catEventAction.Invoke();
}
}
#endregion
}
public class Mouse
{
/// <summary>
/// 老鼠跑了
/// </summary>
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
public class Dog
{
/// <summary>
/// 狗叫了
/// </summary>
public void Bark()
{
Console.WriteLine("狗叫了");
}
}
public class Baby
{
/// <summary>
/// 小孩哭了
/// </summary>
public void Cry()
{
Console.WriteLine("小孩哭了");
}
}
复制代码
调用:
static void Main(string[] args)
{
Cat cat = new Cat();
#region 事件
cat.catEventAction += cat.Meow;
cat.catEventAction += new Mouse().Run;
cat.catEventAction += new Dog().Bark;
cat.catEventAction += new Baby().Cry;
cat.CatEventActionMethod();
#endregion
}
复制代码
执行结果:
②.面向对象实现----从程序设计层面支持
----定义一个接口,所有的类都继承这个接口并实现里面的共通方法,最后在执行方法中循环并调用
使用到的类:
/// <summary>
/// 定义一个通用接口(第二种观察者模式)
/// </summary>
public interface IObject
{
void DoAction();
}
public class Cat : IObject
{
/// <summary>
/// 猫叫了
/// </summary>
public void Meow()
{
Console.WriteLine("猫叫了");
}
#region 第二种观察者模式
public List<IObject> observerlist = new List<IObject>();
public void CatObserver()
{
Console.WriteLine("开始蝴蝶效应了");
if (observerlist.Count > 0)
{
foreach (var item in observerlist)
{
item.DoAction();
}
}
}
public void DoAction()
{
this.Meow();
}
#endregion
}
public class Mouse : IObject
{
public void DoAction()
{
this.Run();
}
/// <summary>
/// 老鼠跑了
/// </summary>
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
public class Dog : IObject
{
/// <summary>
/// 狗叫了
/// </summary>
public void Bark()
{
Console.WriteLine("狗叫了");
}
public void DoAction()
{
this.Bark();
}
}
public class Baby : IObject
{
/// <summary>
/// 小孩哭了
/// </summary>
public void Cry()
{
Console.WriteLine("小孩哭了");
}
public void DoAction()
{
this.Cry();
}
}
复制代码
调用:
static void Main(string[] args)
{
Cat cat = new Cat();
#region 第二种观察者模式
cat.observerlist.Add(new Cat());
cat.observerlist.Add(new Mouse());
cat.observerlist.Add(new Dog());
cat.observerlist.Add(new Baby());
cat.CatObserver();
#endregion
}
复制代码
执行结果:
八、标准的事件定义(简单的事件演示)
使用到的Demo类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyDelegateProject
{
/// <summary>
/// 第一步创建一个类
/// 关注我的公众号
/// </summary>
public class EventDelegateDemo
{
/// <summary>
/// 发布者的实例
/// </summary>
private static PublicNumber publicNumber = new PublicNumber()
{
Id = 1,
//公众号名字
Name = "小张的琐碎日记"
};
/// <summary>
/// 订阅:关联发布者和订阅者之间的关系(初始化他们的关系)
/// </summary>
public static void Subscribe()
{
#region 建立发布者和订阅者之间的关系
//订阅者的实例
Friends friends = new Friends()
{
Id = 1,
Name = "美女"
};
//订阅者的实例
Others others = new Others()
{
Id = 1,
Name = "路人"
};
//建立发布者和订阅者之间的关系
publicNumber.Push += friends.Read;
publicNumber.Push += others.ReadAndAddFriends;
#endregion
}
public static void Publish()
{
//公众号中发布一篇文章
publicNumber.PublishArticles();
}
}
/// <summary>
/// 公众号类
/// 发布者:对外发布事件,当触发一个动作后触发事件
/// </summary>
public class PublicNumber
{
/// <summary>
/// 公众号的ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// 公众号的名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 发布者的动作,发布文章
/// </summary>
public void PublishArticles()
{
MessageInfo messageInfo = new MessageInfo()
{
Id=1,
Title= "关于事件观察者这件事",
Description="这是这篇文章的内容",
VXCode="ThisisVXCode"
};
Console.WriteLine($"发布一篇标题为《{messageInfo.Title}》的文章");
Push.Invoke(this,messageInfo);
}
/// <summary>
///
/// </summary>
public event EventHandler Push;
}
/// <summary>
/// 关注公众号的人(朋友)
/// 事件观察者模式的三大要素:订阅者:对发布的事情表示关注
/// </summary>
public class Friends
{
public int Id { get; set; }
/// <summary>
/// 朋友的名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 浏览公众号信息的动作
/// </summary>
public void Read(object? sender, EventArgs e)
{
MessageInfo mi=(MessageInfo)e ;
Console.WriteLine($"朋友们观看了:Title:{mi.Title},Description:{mi.Description}");
}
}
/// <summary>
/// 关注公众号的人(其他人)
/// 事件观察者模式的三大要素:订阅者:对发布的事情表示关注
/// </summary>
public class Others
{
public int Id { get; set; }
/// <summary>
/// 老师的名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 增加新人动作
/// </summary>
public void ReadAndAddFriends(object? sender, EventArgs e)
{
MessageInfo mi = (MessageInfo)e;
Console.WriteLine($"陌生人观看了:Title:{mi.Title},Description:{mi.Description},并加作者微信:{mi.VXCode}");
}
}
/// <summary>
/// 公众号推送的内容
/// </summary>
public class MessageInfo : EventArgs
{
public int Id { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 内容
/// </summary>
public string Description{get;set;}
/// <summary>
/// 作者微信号
/// </summary>
public string VXCode { get; set; }
}
}
复制代码
调用方法:
static void Main(string[] args)
{
//第一步 初始化发布者和订阅者之间的关系
EventDelegateDemo.Subscribe();
//第二步 执行事件
EventDelegateDemo.Publish();
}
Comments | NOTHING