1.什么是委托
概念
委托的声明和方法一样,仅仅多了一个Delegate关键字;
委托的本质:
- 委托其实是一个类;
- 既然委托是一个类,那我们就可以实例化~~
- 只是委托在实例化的时候要求我们传递一个方法进去;
举例
声明
public delegate void First(); //空方法委托
public delegate void Second(string sayString); //带参数委托
public delegate int Third(); //带返回值委托
public delegate int Fourth(out string outstr, ref string refstr);//带参数带返回值委托
public delegate void Fifth(string sayHi); //参数委托
使用
public void Do()
{
First first = new First(FirstMethod);
first.Invoke();
Second second = new Second(SecondMethod);
second.Invoke("你好");
Third third = new Third(ThirdMethod);
int tResult = third.Invoke();
Fourth fourth = new Fourth(FourthMethod);
string outstr = "";
string refstr = "";
fourth.Invoke(out outstr, ref refstr);
//把委托当做参数传递
Fifth fifth = new Fifth(FifthMethod);
SayHi("小安", fifth);
}
public void FirstMethod()
{
Console.WriteLine("That is first delegate");
}
public void SecondMethod(string sayString)
{
Console.WriteLine($"That is second delegate:{sayString}");
}
public int ThirdMethod()
{
Console.WriteLine("That is third delegate");
return default(int);
}
public int FourthMethod(out string outstr, ref string refstr)
{
outstr = "1";
outstr += "outstr:增加后";
refstr += "refstr:增加后";
Console.WriteLine("That is fouth delegate");
return default(int);
}
public void FifthMethod(string name)
{
Console.WriteLine($"That is fifth delegate:{name}");
}
public void SayHi(string name, Fifth fifth)
{
fifth.Invoke(name);
}
public void SayHiTaowa(string name, Fifth fifth)
{
fifth.Invoke(name);
}
2.委托作用
- 代码重用,去掉重复代码。
- 逻辑解耦。
上面两个观点通过两个例子进行展示;
比如打招呼的一个方法;
ps:子曾经曰过;不要重复造轮子,所以我把老师讲的内容搬来了。
//如果业务比较简单,这二者其实没啥优劣;
//1. 问好的时候,就是招招手~
// 方案1:一句代码搞定了。
// 方案2:每个方法都需要增加 招招手的业务逻辑;
//2. 如果现在再增加一个来自于WuHan的人: 问候就是:吃了么。。。
// 方案1:增加了分支结构,就修改了方法内部的代码,不推荐,方法修改,就需要重新调试测试;
// 方案2:增加了一个独立的方法,对其他方法完全没啥影响。
//如果说两个场景同时存在;
//有没有让鱼与熊掌兼得的方案?
//方案1 传递枚举是为了选择业务逻辑;既然是为了选择业务逻辑,那就直接传递逻辑呗;怎么传递业务逻辑?
//业务逻辑不就时在方法里面?怎么传递方法?不就是委托么?
//委托究竟怎么使用呢?
//如果你在工作中遇到代码耦合很严重; 逻辑和逻辑之间应该分开,但是放在同一个方法内部的;
//如果你的代码中出现了很多重复代码,考虑考虑委托;
//通过委托传递业务逻辑以后:
//一.增加公共业务逻辑方便,只需要在一个方法内部增加即可,去掉重复代码。。
//二.如果新增一个来自武汉的人,那么逻辑由调用者提供;做到了逻辑解耦;
//方法1,增加公共代码方便,但是如果新增加其它地方同学的话,需要增加方法中业务逻辑。
public void SayHi(string name, FromType fromType)
{
Console.WriteLine("招招手~~");
switch (fromType)
{
case FromType.YiChang:
Console.WriteLine($"{name}同学:你好~");
break;
case FromType.ShangHai:
Console.WriteLine($"{name}同学:侬好~");
break;
case FromType.GuangDong:
Console.WriteLine($"{name}同学:雷猴~");
break;
case FromType.WuHan:
Console.WriteLine($"{name}同学:吃了么~");
break;
default:
throw new Exception("fromType Wrong!"); //不要隐藏错误
}
}
//方法2,每增加一个不同地方就要新写一个方法,但是方法独立,需要加公共代码就费劲了。
public void SayHiYiChang(string name)
{
//Console.WriteLine("招招手~~");
Console.WriteLine($"{name}同学:你好~");
}
public void SayHiShangHai(string name)
{
//Console.WriteLine("招招手~~");
Console.WriteLine($"{name}同学:侬好~");
}
public delegate void SayHiDelegate(string name);
public class Students {
//方法3、新建一个委托,把方法作为参数传入,再通过Invoke调用。
public void SayHiAction(string name, SayHiDelegate sayHiDelegate)
{
Console.WriteLine("问候的同时,会招手。。。");
sayHiDelegate.Invoke(name);
}
public void SayHiAction(string name, Action<string> action)
{
Console.WriteLine("问候的同时,会招手。。。");
action.Invoke(name);
}
}
3.委托的多种实例化
{
//直接传入方法,不带()的
NoReturnNoPara noReturnNoPara = new NoReturnNoPara(this.NoReturnNoParaMehtod);
noReturnNoPara.Invoke();
}
{
//静态方法
NoReturnNoPara noReturnNoPara = new NoReturnNoPara(DoNothingStatic);
noReturnNoPara.Invoke();
}
{
//实例化类,传入方法
NoReturnNoPara noReturnNoPara = new NoReturnNoPara(new Student().Study);
noReturnNoPara.Invoke();
}
{
//类中静态方法
NoReturnNoPara noReturnNoPara = new NoReturnNoPara(Student.StudyAdvanced);
noReturnNoPara.Invoke();
}
4.系统提供委托
系统提供的委托有两种;
- Action
- Func
{
Console.WriteLine(" ******Action/Func*************");
//Action:是系统给我们提供的一个委托;是一个没有返回值,可以有参数的委托;最多可以有16个泛型参数;
//Action method = new Action(this.DoNothing);
Action<int> method = new Action<int>(DoNothingInt);
Action<int, string> action = new Action<int, string>(DoNothingIntAndString);
Action<int, string> action1 = DoNothingIntAndString; //这是编译器提供的便捷功能---语法糖;
Action<int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object> action2 = null;//最多支持16个泛型参数;
//Func:是系统给我们提供的一个委托;是一一个必须有返回值的委托,可以有参数的委托;最多可以有16个泛型参数;
Func<int> func = new Func<int>(ToInt);
Func<int, string> func1 = null;
Func<int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, string> func2 = null;//最多支持16个泛型参数;
//为什么框架要给我们提供这两个委托呢?既然是框架提供的委托,自然是希望我们就使用者两个委托:
//举例自定委托不好处;
new Thread(new ParameterizedThreadStart(DoObject));
ThreadPool.QueueUserWorkItem(new WaitCallback(DoObject));
new Thread(new WaitCallback(DoObject));//这句报错了。
//为什么? 委托的本质其实是一个类;定义的不同委托其实是不同的类,这些类没有父子级关系;不能替换,但是这些委托参数和返回值又都是一致的;
//后面大家在使用委托的时候,就不要自己定义了,直接使用Action、Func;为了避免大家在开发工作中过多的定义委托;其实是个冗余;
//那为什么我们还是可以自定义委托呢? 因为历史原因,因为在以前的框架中,使用了各种不同的委托;
//C#是向前兼容;这是历史包袱;
//在后面的作业中,就不要自己去定义委托了,必须使用acton、func;
}
如果需要在系统上委托自定,只需要加一个就可以了。
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>(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);
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, 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);
5.多播委托
- 可以在实例化的时候通过+=添加多个方法形成方法链子;
- 可以在实例化的时候通过-=移除方法;
代码实现
//Console.WriteLine(" ******多播委托*************");
多播委托:我们声明的任何一个委托都是多播委托;
多播委托可以在实例化以后,可以通过+=添加和当前委托参数返回值完全一致的多个方法;形成方法链,在调用Invoke方法的时候,会按照+=的顺序依次执行方法;
NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);
method += this.DoNothing;
method += DoNothingStatic;
method += new Student().Study;//不能减去,因为是不同的Student实例
method += Student.StudyAdvanced;
method += () => {
Console.WriteLine("this is Lamda"); }; //lambda表达式在中间语言IL中还是生成不同的两个方法,生成的时候其实会有两个不同的名字
//method.Invoke();
多播委托:可以通过-=移除方法(必须是移除同一个方法),是按照从下往上按照顺序依次匹配,如果匹配不到,就啥也不干,只要是配到一个就移除掉,且只能移除一个;
method -= this.DoNothing;
method -= this.DoNothing;
method -= DoNothingStatic;
method -= new Student().Study;//不能减去,因为是不同的Student实例
method -= Student.StudyAdvanced;
method -= () => {
Console.WriteLine("this is Lamda"); };//lambda表达式在中间语言IL中还是生成不同的两个方法,生成的时候其实会有两个不同的名字
//如果参数不一样:
method.Invoke();
//method.BeginInvoke(null,null); //开启一个新的线程去完成计算;多播委托不允许异步;
foreach (NoReturnNoPara item in method.GetInvocationList())
{
item.BeginInvoke(null, null);
item.Invoke();
}
6.事件Event / 观察者模式
首先看下Action,就相当于一个委托。
public delegate void Action();
事件如何声明?
/// <summary>
///事件:事件和委托好像没太大变化,只是在委托的前面加了Event 关键;
///事件和委托的区别?
///
/// 事件其实是委托+event 关键字;事件只能在声明当前这事件的类的内部Invoke;即时是在子类也不行;
/// 这样就更加安全;
/// 委托是一个类;事件其实是委托的一个实例; 事件是比委托更加安全;
///
/// 事件的应用:真的是无处不在,AspNetCoreMVC管道;Winform,WPF;
/// </summary>
public event Action ActionEventHanlder;
使用事件+=方法,因事件不能直接调用,所以要在实例类中Invoke();
{
Console.WriteLine(" ********事件的应用***********");
Cat cat = new Cat();
cat.ActionEventHanlder += new Dog().Wang; //狗叫了
cat.ActionEventHanlder += new Mouse().Run;//老鼠跑了
cat.ActionEventHanlder += new Baby().Cry; // 小孩哭了
cat.ActionEventHanlder += new Mother().Wispher;
cat.ActionEventHanlder += new Father().Roar;
cat.ActionEventHanlder += new Neighbor().Awake;
cat.ActionEventHanlder += new Stealer().Hide;
//cat.ActionEventHanlder.Invoke();
cat.MiaoEventHanlder();
}
观察者模式
{
Console.WriteLine(" ******面向对象的思想实现观察者*************");
Cat cat = new Cat();
cat.AddObserver(new Dog()); //狗叫了
cat.AddObserver(new Mouse());//老鼠跑了
cat.AddObserver(new Baby()); // 小孩哭了
cat.AddObserver(new Mother());
cat.AddObserver(new Father());
cat.AddObserver(new Neighbor());
cat.AddObserver(new Stealer());
cat.MiaoObserver();
}
{
//Cat类中的方法
public List<IObject> ObserverList = new List<IObject>();
public void AddObserver(IObject observer)
{
ObserverList.Add(observer);
}
public void MiaoObserver()
{
Console.WriteLine("{0} MiaoObserver", this.GetType().Name);
foreach (IObject observer in ObserverList)
{
observer.DoAction();
}
}
}
7.手写标准事件
public class EventStandard
{
public static void Show()
{
FrameworkClass frameworkClass = new FrameworkClass()
{
Id = 1,
Name = "VIP课程"
};
//订阅:就是个订户和事件发布者关联起来
frameworkClass.PriceIncrease += new StudentInfo().Buy;
frameworkClass.PriceIncrease += new Tencent().Extension;
frameworkClass.Price = 6299;
}
/// <summary>
/// 订户: 关注事件,事件触发后,自己做出相应的动作
/// </summary>
public class StudentInfo
{
public void Buy(object sender, EventArgs e)
{
FrameworkClass frameworkClass = (FrameworkClass)sender;
FrameworkEventArge eventArge = (FrameworkEventArge)e;
Console.WriteLine($"this is {frameworkClass.Name}");
Console.WriteLine($"{frameworkClass.Name}之前的价格是{eventArge.OldPrice}");
Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
Console.WriteLine("果断买了。。。");
Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
}
}
/// <summary>
/// 订户,关注事件,事件触发之前,做出点动作
/// </summary>
public class Tencent
{
public void Extension(object sender, EventArgs e)
{
FrameworkClass frameworkClass = (FrameworkClass)sender;
FrameworkEventArge eventArge = (FrameworkEventArge)e;
Console.WriteLine($"this is {frameworkClass.Name}");
Console.WriteLine($"{frameworkClass.Name}之前的价格是{eventArge.OldPrice}");
Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
Console.WriteLine("马上推广一波。。");
}
}
public class FrameworkEventArge : EventArgs
{
public int OldPrice {
get; set; }
public int NewPrice {
get; set; }
}
/// <summary>
/// 事件的发布者,发布是按并且在满足条件的情况下,触发事件
/// </summary>
public class FrameworkClass
{
public int Id {
get; set; }
public string Name {
get; set; }
private int _price=5299;
public int Price
{
get
{
return _price;
}
set
{
if (value > _price)
{
///触发一部分动作
PriceIncrease.Invoke(this, new FrameworkEventArge()
{
OldPrice = _price,
NewPrice = value
});
}
_price = value;
}
}
public event EventHandler PriceIncrease;
}
}