基本知识点:委托的声明、实例化、调用方法
1.委托的声明
委托既可以声明在类的外边,也可以声明在类的内部,委托与抽象方法很像,有返回值也有参数,用一个delegate关键字修饰
2.通过IIL反编译可以看到委托就是一个类,继承自MulticastDelegate(多播委托),MulticastDelegate是一个特殊的类,我们无法继承
3.观察VS编辑器,类名和委托名同一个颜色,而方法名却是另一个颜色,这也佐证了委托其实就是一个类
public delegate void NoReturnNoParaOutClass();
public class CustomDelegate
{
public delegate void NoReturnNoPara();
public delegate void NoReturnWithPara(int x, int y);
public delegate string WithReturnNoPara();
public delegate string WithReturnWithPara(int x, int y);
public void ShowBasicInfo()
{
Console.WriteLine();
Console.WriteLine("------------------基本知识点:委托的声明、实例化、调用方法-------------------");
Console.WriteLine();
//委托的实例化:委托可以实例化,需要传递一个方法进来,但是必须严格要求传递一个委托参数、返回值完全一致的方法
NoReturnNoParaOutClass noReturnNoParaOutClass = new NoReturnNoParaOutClass(new AllMethods().NoReturnNoParaMethod);
noReturnNoParaOutClass.Invoke();
Console.WriteLine();
NoReturnNoPara noReturnNoPara = new NoReturnNoPara(new AllMethods().NoReturnNoParaMethod);
noReturnNoPara.Invoke();//执行这个委托,就把当前传递给委托的这个方法执行一下
//noReturnNoPara.BeginInvoke(null, null);//这里是开启一个新的线程去执行方法
//但是在.NetCore中不支持Action.BeginInvoke的委托异步调用方法
//noReturnNoPara.EndInvoke();//需要method.BeginInvoke的执行结果作为参数
Console.WriteLine();
//在实例化的时候,要求传递进来的方法结构和委托完全一致,要求参数和返回值保持一致
NoReturnWithPara noReturnWithPara = new NoReturnWithPara(new AllMethods().NoReturnWithParaMethod);
noReturnWithPara.Invoke(1, 2);
Console.WriteLine();
WithReturnNoPara withReturnNoPara = new WithReturnNoPara(new AllMethods().WithReturnNoParaMethod);
string noParaResult = withReturnNoPara.Invoke();
Console.WriteLine(noParaResult);
Console.WriteLine();
WithReturnWithPara withReturnWithPara = new WithReturnWithPara(new AllMethods().WithReturnWithParaMethod);
string withParaResult = withReturnWithPara.Invoke(251, 357);
Console.WriteLine(withParaResult);
Console.WriteLine("委托的本质是什么?");
Console.WriteLine("委托的本质其实就是一个类,继承自MulticastDelegate,MulticastDelegate是一个特殊的类,我们无法继承这个类");
}
}
基本知识点:委托的多种实例化方法
public void ShowInstantiation()
{
Console.WriteLine();
Console.WriteLine("--------------基本知识点:委托的多种实例化方法--------------");
Console.WriteLine();
NoReturnNoPara noreturnnopara1 = new NoReturnNoPara(new AllMethods().NoReturnNoParaMethod);
noreturnnopara1.Invoke();
Console.WriteLine();
Student student = new Student()
{
Id = 123,
Name = "渡边",
ClassId = 2,
Age = 23
};
NoReturnNoPara noreturnnopara2 = new NoReturnNoPara(student.Study);
noreturnnopara2.Invoke();
Console.WriteLine();
NoReturnNoPara noreturnnopara3 = new NoReturnNoPara(Student.StudyAdvanced);
noreturnnopara3.Invoke();
Console.WriteLine();
NoReturnNoPara noreturnnopara4 = new NoReturnNoPara(AllMethods.DoNothingStatic);
noreturnnopara4.Invoke();
Console.WriteLine();
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int ClassId { get; set; }
public int Age { get; set; }
public void Study()
{
Console.WriteLine($"{Name}正在学习.net高级课");
}
public static void StudyAdvanced()
{
Console.WriteLine($"学习.net高级班vip课");
}
}
基本知识点:多播委托
其实我们上面声明的任何一个委托都是多播委托
多播委托可以在实例化以后,通过+=添加和当前委托的参数、返回值类型完全一致的多个方法,形成方法链,在调用Invoke方法的时候,会按照+=的顺序依次执行各个方法
多播委托还可以通过-=移除方法,移除的必须是同一个方法,是按照从下往上的顺序依次匹配,只要匹配到一个就移除一个,而且只能移除一个,如果匹配不到,就啥也不干,也不会报错。
问题
对于方法名一样但参数不一样(即重载方法)使用委托会存在问题吗?
不会有问题的,因为声明委托的时候已经规定了委托的参数和返回值类型,而我们通过委托添加的方法,委托和返回值必须一致,因此不会存在问题。
public void ShowMulticastDelegate()
{
Console.WriteLine();
Console.WriteLine("-----------基本知识点:多播委托------------");
Console.WriteLine();
Student student = new Student()
{
Id = 10,
Name = "渡边",
ClassId = 1,
Age = 20
};
NoReturnNoPara noreturnnopara = new NoReturnNoPara(new AllMethods().NoReturnNoParaMethod);
noreturnnopara += new AllMethods().NoReturnNoParaMethod;
noreturnnopara += AllMethods.DoNothingStatic;
noreturnnopara += Student.StudyAdvanced;
noreturnnopara += new Student()
{
Id = 15,
Name = "渡边",
ClassId = 1,
Age = 20
}.Study;
noreturnnopara += student.Study;
//Lambda表达式在中间语言IL中还是生成不同的两个方法,生成的时候其实会有两个不同的名字
noreturnnopara += () => { Console.WriteLine("执行Lambda表达式"); };
noreturnnopara -= new AllMethods().NoReturnNoParaMethod;
noreturnnopara -= AllMethods.DoNothingStatic;
noreturnnopara -= Student.StudyAdvanced;
noreturnnopara -= new Student()
{
Id = 15,
Name = "渡边",
ClassId = 1,
Age = 20
}.Study;//因为这个Student对象跟上边的Student对象不是一个了
noreturnnopara -= student.Study;
noreturnnopara -= () => { Console.WriteLine("执行Lambda表达式"); };
noreturnnopara.Invoke();
//noreturnnopara.BeginInvoke(null, null);//开启一个新的线程去完成计算,多播委托不允许异步
Console.WriteLine();
foreach(NoReturnNoPara item in noreturnnopara.GetInvocationList())
{
//item.BeginInvoke(null, null);//这样执行异步操作,但是.NetCore不支持BeginInvoke?
//报错:System.PlatformNotSupportedException:“Operation is not supported on this platform.”
item.Invoke();
}
}
基本知识点:Action和Func
Action的本质就是系统定义的一个委托,是一个“没有返回值”、“可以有参数”的委托
Action的参数最多支持16个,如果超出16个,可以自定义Action的参数
Func可以有返回值,也可以带参数
Func的参数最多可以有16+1个,前16个是参数类型,后边1个是返回值类型,如果需要17个及以上的参数,可以自己定义
为什么框架要给我们提供这两个委托呢?
既然是框架提供的委托,自然是希望我们就是用这个委托
以下两者,都需要带一个参数但没有返回值的委托
new Thread(new ParameterizedThreadStart(new AllMethods().DoObject));
ThreadPool.QueueUserWorkItem(new WaitCallback(new AllMethods().DoObject));
new Thread(new WaitCallback(new AllMethods().DoObject));
同样是需要一个object参数且没有返回值的委托,为什么上边会报错?
因为委托的本质其实是一个类,定义的不同委托其实是不同的类,这些类没有父子级别关系,不能替换
后边在使用委托的时候,就不要自己定义了,直接使用Action、Func,这两个基本就够满足所有委托的需求了,使用Action、Func其实也是为了避免在开发工作中过多地定义委托,其实是个冗余
问题
那为什么还可以自己定义委托呢?
因为历史原因,在以前的框架中使用了各种不同的委托,C#是向前兼容的,这是历史包袱
public void ShowActionFunc()
{
Console.WriteLine();
Console.WriteLine("--------------基本知识点:Action和Func---------------");
Console.WriteLine();
Student student = new Student()
{
Id = 10,
Name = "绿子",
ClassId = 1,
Age = 20
};
Console.WriteLine("Action的本质就是系统定义的一个委托,是一个“没有返回值”、可以有参数的委托");
Console.WriteLine();
Action action = new Action(new AllMethods().NoReturnNoParaMethod);
action += new AllMethods().NoReturnNoParaMethod;
action += new Student()
{
Id = 12,
Name = "渡边",
ClassId = 2,
Age = 21
}.Study;
action += student.Study;
action += Student.StudyAdvanced;
action += AllMethods.DoNothingStatic;
action += () => { Console.WriteLine("执行了Lambda表达式"); };
action -= new AllMethods().NoReturnNoParaMethod;
action -= new Student()
{
Id = 12,
Name = "渡边",
ClassId = 2,
Age = 21
}.Study;
action -= student.Study;
action -= Student.StudyAdvanced;
action -= AllMethods.DoNothingStatic;
action -= () => { Console.WriteLine("执行了Lambda表达式"); };
action.Invoke();
Console.WriteLine();
Action<int, int> action1 = new Action<int, int>(new AllMethods().NoReturnWithParaMethod);
action1.Invoke(12523, 9463);
Console.WriteLine();
Action<int, string, DateTime> action2 = new Action<int, string, DateTime>(new AllMethods().DoNothingComplex);
action2.Invoke(8323, "渡边", DateTime.Now);
Console.WriteLine();
//Action的参数最多支持16个,如果超出16个,可以自定义Action的参数
Action<int, string, DateTime> action3 = new AllMethods().DoNothingComplex;
action3.Invoke(24924, "直子", DateTime.Now);
Console.WriteLine();
//Func可以有返回值,也可以带参数
Func<int, int, string> func1 = new Func<int, int, string>(new AllMethods().WithReturnWithParaMethod);
func1.Invoke(432, 983);
Console.WriteLine();
//第一个参数是参数类型,第二个参数是返回值类型
//Func<int, string> func2 = new Func<int, string>(this.DoNothingToString);
}
简单的“俄罗斯套娃”
public class CustomDelegateExtendsion
{
public void ShowSimpleDools()
{
InvokerAction invoker = new InvokerAction();
Type type = invoker.GetType();
MethodInfo method = type.GetMethod("Method");
//已经通过反射找到了Method方法,即要执行的核心业务逻辑
//以下仅仅是包装了一个委托,需要通过action.Invoke()才会真的执行对应的方法
Action action = new Action(() =>
{
method.Invoke(invoker, null);
});
if (method.IsDefined(typeof(BeforeMethodAttribute), true))
{
BeforeMethodAttribute attribute = method.GetCustomAttribute<BeforeMethodAttribute>();
Action actionResult = attribute.Do(action);
actionResult.Invoke();
}
Console.WriteLine();
if (method.IsDefined(typeof(BeforeMethodLogAttribute), true))
{
BeforeMethodLogAttribute attribute = method.GetCustomAttribute<BeforeMethodLogAttribute>();
Action actionResult = attribute.Do(action);
actionResult.Invoke();
}
Console.WriteLine("------------------------------------");
foreach(AbstractAttribute attribute in method.GetCustomAttributes())
{
Action actionResult = attribute.Do(action);
actionResult.Invoke();
Console.WriteLine();
}
}
}
public class InvokerAction
{
/// <summary>
/// 核心业务逻辑
/// 如果想在此之前做点什么事情,而且希望前面做的事情能够自由装配
/// </summary>
[BeforeMethod]
[BeforeMethodLog]
public void Method()
{
Console.WriteLine("这里是要执行的核心业务逻辑");
}
}
[AttributeUsage(AttributeTargets.Method,AllowMultiple = true,Inherited = true)]
public abstract class AbstractAttribute : Attribute
{
public abstract Action Do(Action action);
}
public class BeforeMethodAttribute : AbstractAttribute
{
public override Action Do(Action action)
{
Action actionResult = new Action(() =>
{
Console.WriteLine("在核心业务执行之前执行的业务逻辑");//这里才是核心业务逻辑前增强的业务逻辑,可以随便写
action.Invoke();
});
return actionResult;
}
}
public class BeforeMethodLogAttribute : AbstractAttribute
{
public override Action Do(Action action)
{
Action actionResult = new Action(() =>
{
Console.WriteLine("在核心业务逻辑前打印日志");
action.Invoke();
});
return actionResult;
}
}
多播委托的应用
public interface IObject
{
void DoAction();
}
public class Dog : IObject
{
public void DoAction()
{
this.Wang();
}
public void Wang()
{
Console.WriteLine("{0} Wang~Wang~", this.GetType().Name);
}
}
public class Mouse : IObject
{
public void DoAction()
{
this.Run();
}
public void Run()
{
Console.WriteLine("{0} Run", this.GetType().Name);
}
}
public class Baby : IObject
{
public void DoAction()
{
this.Cry();
}
public void Cry()
{
Console.WriteLine($"{0} Cry", this.GetType().Name);
}
}
public class Father : IObject
{
public void DoAction()
{
this.Roar();
}
public void Roar()
{
Console.WriteLine("{0} Roar", this.GetType().Name);
}
}
public class Mother : IObject
{
public void DoAction()
{
this.Wispher();
}
public void Wispher()
{
Console.WriteLine("{0} Wispher", this.GetType().Name);
}
}
public class Neighbor : IObject
{
public void DoAction()
{
this.Awake();
}
public void Awake()
{
Console.WriteLine("{0} Awake", this.GetType().Name);
}
}
public class Stealer : IObject
{
public void DoAction()
{
this.Hide();
}
public void Hide()
{
Console.WriteLine("{0} Hide", this.GetType().Name);
}
}
/// <summary>
/// 一只猫的故事……
/// 在一个月黑风高的晚上
/// 一只猫叫了一声,然后
/// 狗叫了,老鼠跑了……
/// </summary>
public class Cat
{
/// <summary>
/// 代码中依赖太重,中间涉及到Dog等,任何一个修改,都需要修改猫,会造成这只猫非常地不稳定
/// 这根本就不是我们写代码的正确姿势
/// 猫就是猫,只是负责自己的事情,只能喵一声,其他动作跟猫完全无关
/// 在这里猫只是喵一声,这仅仅是一个触发动作,后续究竟要做什么,猫不用管
/// </summary>
public void Miao()
{
Console.WriteLine($"{this.GetType().Name}Miao~wu~");
new Dog().Wang();
new Mouse().Run();
new Baby().Cry();
new Mother().Wispher();
new Father().Roar();
new Neighbor().Awake();
new Stealer().Hide();
}
/// <summary>
/// 正确的姿势:应该是猫叫了以后,触发一部分动作,这部分动作不会影响到猫
/// 触发一部分动作其实就是去执行一连串方法,那该怎么办?——多播委托
///
/// 发现通过应用多播委托以后,猫变得稳定了,无论Dog怎么修改,都不会影响到猫
/// 实现了让猫只是做自己的事情,其它的事情丢给外部去指定,代码稳定了
/// </summary>
public Action ActionHandler;
public void MiaoByDelegate()
{
Console.WriteLine($"{this.GetType().Name}喵了一声,并通过多播委托触发了以下一系列动作……");
//if (ActionHandler != null)
//{
// ActionHandler.Invoke();
//}
//这个问号表示,如果ActionHandler不为空就执行Invoke方法,效果同上面的if相同
ActionHandler?.Invoke();
Console.WriteLine("喵完就跑……");
}
/// <summary>
/// 事件:事件和委托好像没有太大变化,只是在委托的前面加了event关键字
/// 事件和委托的区别?
/// 事件其实是委托加一个event关键字,事件只能在声明当前事件的类内部Invoke
/// 即使是在子类也不可以
/// 这样就更加安全
/// 委托是一个类,事件其实是委托的一个实例,只是事件比委托更加安全
///
/// 事件的应用真的是无处不在,AspNetCoreMVC管道、WinForm
/// </summary>
public event Action EventHandler;
public void MiaoByEvent()
{
Console.WriteLine($"{this.GetType().Name}喵了一声,并通过事件触发了以下一系列动作……");
if (EventHandler != null)
{
EventHandler.Invoke();
}
EventHandler?.Invoke();
Console.WriteLine("喵完赶紧跑……");
}
/// <summary>
/// 观察者模式:甩锅大法,就是把不稳定的东西都丢出去,保证自身稳定
/// </summary>
private List<IObject> list = new List<IObject>();
public void AddObserver(IObject observer)
{
list.Add(observer);
}
public void MiaoByObserver()
{
Console.WriteLine($"{this.GetType().Name}喵了一声,并通过观察者模式触发了一系列动作……");
foreach(IObject obj in list)
{
obj.DoAction();
}
Console.WriteLine("喵完 赶紧、立刻、马上 跑……");
}
}
public void ShowUseMuliDele()
{
Console.WriteLine();
Console.WriteLine("----------------最烂的代码写法----------------");
Console.WriteLine();
new Cat().Miao();
Console.WriteLine();
Console.WriteLine("----------------多播委托----------------");
Console.WriteLine();
Cat cat = new Cat();
Action action = cat.ActionHandler;
Action action = new Action(new Dog().DoAction);
action += new Mouse().DoAction;
action += new Baby().DoAction;
action += new Mother().DoAction;
action += new Father().DoAction;
action += new Neighbor().DoAction;
action += new Stealer().DoAction;
cat.ActionHandler = action;
cat.MiaoByDelegate();
Console.WriteLine();
Console.WriteLine("----------------事件----------------");
Console.WriteLine();
cat.EventHandler += new Dog().DoAction;
cat.EventHandler += new Mouse().DoAction;
cat.EventHandler += new Baby().DoAction;
cat.EventHandler += new Mother().DoAction;
cat.EventHandler += new Father().DoAction;
cat.EventHandler += new Neighbor().DoAction;
cat.EventHandler += new Stealer().DoAction;
cat.MiaoByEvent();
Console.WriteLine();
Console.WriteLine("----------------观察者模式----------------");
Console.WriteLine();
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.MiaoByObserver();
}
简单的标准事件订阅与发布
public class EventStandard
{
public void Show()
{
Console.WriteLine();
FrameworkClass frameworkClass = new FrameworkClass()
{
Id = 123,
Name = "朝夕教育VIP架构班"
};
//订阅:就是把订户和事件发布者关联起来
frameworkClass.Click_PriceIncrease += new StudentInfo().Buy;
frameworkClass.Click_PriceIncrease += new TecentInfo().Extension;
frameworkClass.Price = 6299;
}
}
public class FrameworkClass
{
public int Id { get; set; }
public string Name { get; set; }
private int _price = 5299;
public event EventHandler Click_PriceIncrease;
public int Price
{
get
{
return _price;
}
set
{
//架构班涨价
if (value > _price)
{
//触发一部分动作
Click_PriceIncrease.Invoke(this, new FrameworkArgs()
{
OldPrice = _price,
NewPrice = value
});
_price = value;
}
}
}
}
public class FrameworkArgs : EventArgs
{
public int OldPrice { get; set; }
public int NewPrice { get; set; }
}
public class StudentInfo
{
public void Buy(object sender,EventArgs e)
{
FrameworkClass frameworkClass = (FrameworkClass)sender;
FrameworkArgs frameworkArgs = (FrameworkArgs)e;
Console.WriteLine($"{frameworkClass.Name}");
Console.WriteLine($"{frameworkClass.Name}之前的价格是:{frameworkArgs.OldPrice}");
Console.WriteLine($"{frameworkClass.Name}马上要更新的价格是:{frameworkArgs.NewPrice}");
Console.WriteLine("马上买入……好好学习……");
Console.WriteLine($"{frameworkClass.Name}现在的价格是:{frameworkArgs.NewPrice}");
}
}
public class TecentInfo
{
public void Extension(object sender,EventArgs e)
{
FrameworkClass frameworkClass = (FrameworkClass)sender;
FrameworkArgs frameworkArgs = (FrameworkArgs)e;
Console.WriteLine($"{frameworkClass.Name}");
Console.WriteLine($"{frameworkClass.Name}之前的价格是:{frameworkArgs.OldPrice}");
Console.WriteLine($"{frameworkClass.Name}马上要更新的价格是:{frameworkArgs.NewPrice}");
Console.WriteLine("马上推广一波……");
Console.WriteLine($"{frameworkClass.Name}现在的价格是:{frameworkArgs.NewPrice}");
}
}