C# 委托 事件
一:什么叫委托
通过反射发现,委托其实是一个类,继承自System.MulticastDelegate,但是System.MulticastDelegate这个类是特殊类,不能被继承
二:委托的声明
public delegate void NoReturnNoParaOutClass();
public class MyDelete
{
public delegate void NoReturnNoPara<T>(T t);
public delegate void NoReturnNoPara();
public delegate void NoReturnWithPara(int x, int y);
public delegate int WithReturnNoPara();
public delegate string WithReturnWithPara(out int x, ref int y);
}
委托可以声明在类外面,可以声明再类里面
三:委托的实例和调用
private int GetSomething()
{
return ;
}
private int GetSomething2()
{
return ;
} private int GetSomething3()
{
return ;
}
private void DoNothing()
{
Console.WriteLine("This is DoNothing");
}
private static void DoNothingStatic()
{
Console.WriteLine("This is DoNothingStatic");
}
public string ParaReturn(out int x, ref int y)
{
throw new Exception();
}
//多种途径实例化,要求传递一个参数类型,返回值都跟委托一致的方法
{
WithReturnWithPara method = new WithReturnWithPara(ParaReturn);
int x = ;
int y = ;
var dd = method.Invoke(out x, ref y);
}
//begininvoke
{
WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
int iResult = method.Invoke();
iResult = method();
var result = method.BeginInvoke(null, null);//异步调用
method.EndInvoke(result);
}
{
NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);
//委托实力的调用,参数和委托约束的一致
method.Invoke(); //1
//method(); //2
//method.BeginInvoke(null, null); //3
//this.DoNothing(); //1,2,3都等同于this.DoNothing }
{
NoReturnNoPara method = new NoReturnNoPara(DoNothingStatic);
}
{
NoReturnNoPara method = new NoReturnNoPara(Student.StudyAdvanced);
}
{
NoReturnNoPara method = new NoReturnNoPara(new Student().Study);
}
四:为什么要使用委托
有时候我们声明一个方法,直接调用蛮好的,为啥还要使用委托,然后还要先声明,再实例化,再inovke调用呢?
下面我们举个例子,比如一个人问好这件事情,不同人问候方式不一样,我们会先定义一个类型,如枚举
public enum PeopleType
{
Chinese,
America,
Japanese
}
然后通过不同的类型来判断问候方式不同,如下
/// 为不同的人,进行不同的问候
/// 传递变量--判断一下----执行对应的逻辑
/// </summary>
/// <param name="name"></param>
/// <param name="peopleType"></param>
public void SayHi(string name, PeopleType peopleType)
{
switch (peopleType)
{
case PeopleType.Chinese:
Console.WriteLine($"{name}晚上好");
break;
case PeopleType.America:
Console.WriteLine($"{name},good evening");
break;
case PeopleType.Japanese:
Console.WriteLine($"{name},&&%*^^***@@@&&&&");
break;
default:
throw new Exception("wrong peopleType"); //遇到异常报错
}
}
这样做的好处是:以后如果增加公共逻辑等比较容易,但是如果类型比较多,这个方法会变成无限制改动,导致方法难以维护,于是很多人想着增加分支,就增加方法--不影响别的方法的思路来改善
public void SayHiChinese(string name)
{
Console.WriteLine($"{name}晚上好");
}
public void SayHiJapanese(string name)
{
Console.WriteLine($"{name},&&%*^^***@@@&&&&");
}
public void SayHiAmerican(string name)
{
Console.WriteLine($"{name},good evening");
}
然后上层判断调用
这样做的好处是:修改某个方法--不影响别的方法 ,但是缺点却是:增加公共逻辑---多个方法就有很多重复代码
那么我们想:既增加逻辑方便,又维护简单,鱼肉熊掌,如何兼得呢?
我们可以把相应的逻辑做为参数传进来,这样就解决了我们的问题
具体我们可以按照以下来做:
public void SayHiPerfact(string name, SayHiDeletegate method)
{
Console.WriteLine("增加开始日志");
method.Invoke(name);
Console.WriteLine("增加结束日志");
}
public delegate void SayHiDeletegate(string name);
然后调用的时候如下:
SayHiDeletegate method = new SayHiDeletegate(SayHiChinese);
这样就做到了
1:逻辑解耦,方便维护
2:代码重构,去掉重复
其实这也是我们选择使用委托的两大优点
注意:以上我们纯粹为了定义委托而定义委托,其实框架已经我们帮我们定义了Action 和Func这两个委托,Action是没有返回值,Func是有返回值的,这两个委托类已经足够我们使用了,所以有时候我们使用的时候,没有必要自己再去定义,而直接使用即可
1:Action: 系统提供的,0-16个泛型参数,不带返回值的 委托
//Action 系统提供的,0-16个泛型参数,不带返回值的 委托
Action action0 = new Action(DoNothing);
Action action1 = this.DoNothing; //语法糖,就是编译器帮我们添加上new Action
Action<int> action2 = this.ShowInt;
2:Func 系统提供的,0-16个泛型参数,带一个返回值的 委托
//Func 系统提供的,0-16个泛型参数,带返回值的 委托
Func<int> func = this.GetSomething2;
func.Invoke(); Func<int, string> func1 = this.ToString;
func1.Invoke();
3:系统或者框架为什么要封装这样的方法?第一步我们定义一个这样的方法,需要传入一个Action
private void DoAction(Action act)
{
act.Invoke();
}
然后我们可以这样调用:
Action action0 = this.DoNothing;
NoReturnNoPara method = this.DoNothing;
// this.DoAction(method); //这样就不行,因为类型不一致
this.DoAction(action0);
但是我们不能使用 this.DoAction(method),因为:委托的本质是类,action和NoReturnNoPara是不同的类,虽然实例化都可以传递相同的方法,但是没有父子关系,所以不能替换,就像student和teacher两个类,实例化都是传递id/name,但是二者不能替换
所以:框架提供这种封装,自然是希望大家都统一使用Action/Func,但是之前还有很多委托,至今再框架中还能看到,因为.net向前兼容,以前的版本去不掉的,保留着,这是历史包袱,此后我们就不要定义新的委托了!
五:多播委托
委托是一个类,然后继承于MulticastDelegate(多播委托),但是这个类是特殊类,所以任何一个委托都是多播委托类型的子类
{
//多播委托:任何一个委托都是多播委托类型的子类,可以通过+=去添加方法
//多播委托如果中间出现未捕获的异常,方法链直接结束
//多播委托:一个变量保存多个方法,可以增减;invoke的时候可以按顺序执行
//+= 为委托实例按顺序增加方法,形成方法链,Invoke时,按顺序依次执行系列方法
Student studentNew = new Student(); NoReturnNoPara method = new NoReturnNoPara(this.DoNothing); //普通的DoNothing方法
method += new NoReturnNoPara(this.DoNothing); //普通的DoNothing方法
method += new NoReturnNoPara(DoNothingStatic); //静态方法
method += new NoReturnNoPara(Student.StudyAdvanced); //静态方法
method += new NoReturnNoPara(new Student().Study);//不是同一个实例,所以是不同的方法
method += new NoReturnNoPara(studentNew.Study);
method.Invoke();
//method.BeginInvoke(null, null);//委托里面如果有多个Target,则不能异步调用,多播委托是不能异步的 foreach (Action item in method.GetInvocationList()) //得到委托实例的target方法 ,静态方法target为null
{
item.Invoke();
//item.BeginInvoke(null, null);
} //-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除且只移除一个,没有也不异常
method -= new NoReturnNoPara(this.DoNothing);
method -= new NoReturnNoPara(DoNothingStatic);
method -= new NoReturnNoPara(Student.StudyAdvanced);
method -= new NoReturnNoPara(new Student().Study); //去不掉,原因是不同的实例的相同方法,并不吻合
method -= new NoReturnNoPara(studentNew.Study);
method.Invoke();
}
{
WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
method += new WithReturnNoPara(this.GetSomething2);
method += new WithReturnNoPara(this.GetSomething3);
int iResult = method.Invoke();//多播委托带返回值,结果以最后的为准,所以一般多播委托用的是不带返回值的
}
注意:
+= 为委托实例按顺序增加方法,形成方法链,Invoke时,按顺序依次执行系列方法
-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合(new Student().Study如果声明两次则不属于吻合)的,移除且只移除一个,没有也不异常
多播委托其实就是观察者模式的缩影。
比如一只猫叫了一声,然后需要触发一系列的动作,比如老鼠跑,孩子哭,狗叫等等
如果我们把这些都写在猫的miao的方法中,如下:
public void Miao()
{
Console.WriteLine("{0} Miao", this.GetType().Name); new Mouse().Run();
new Baby().Cry();
new Mother().Wispher();
//new Brother().Turn();
new Father().Roar();
new Neighbor().Awake();
new Stealer().Hide();
new Dog().Wang();
}
上面的代码:
依赖太重,依赖多个类型,任何类型的变化都得修改猫
职责耦合,猫不仅自己miao,还得找各种对象执行各种动作甚至控制顺序
任意环节增加或者减少调整顺序, 都得修改猫
那么我们可以修改代码,然后Cat的这类中,增加一个委托,然后猫只是自己叫,具体的其他的动作,只需要执行这个多播委托即可,代码如下:
//猫 叫一声 触发一系列后续动作
//多了个 指定动作 正是这个不稳定 封装出去 甩锅
public MiaoDelegate MiaoDelegateHandler;
public void MiaoNew()
{
Console.WriteLine("{0} MiaoNew", this.GetType().Name);
if (this.MiaoDelegateHandler != null)
{
this.MiaoDelegateHandler.Invoke();
}
}
然后外面调用的时候可以直接通过下面:
{
Cat cat = new Cat();
cat.MiaoDelegateHandler += new MiaoDelegate(new Mouse().Run);
cat.MiaoDelegateHandler += new MiaoDelegate(new Baby().Cry);
cat.MiaoDelegateHandler += new MiaoDelegate(new Mother().Wispher);
cat.MiaoDelegateHandler += new MiaoDelegate(new Brother().Turn);
cat.MiaoDelegateHandler += new MiaoDelegate(new Father().Roar);
cat.MiaoDelegateHandler += new MiaoDelegate(new Neighbor().Awake);
cat.MiaoDelegateHandler += new MiaoDelegate(new Stealer().Hide);
cat.MiaoDelegateHandler += new MiaoDelegate(new Dog().Wang);
cat.MiaoNew();
Console.WriteLine("***************************");
}
这样猫就减少了依赖,而且猫叫后,各种动作的顺序也可以随便改变,而不会改变猫叫的方法!
六:事件
事件:是带event关键字的委托的实例,event可以限制变量被外部调用/直接赋值
event:限制权限,只允许在事件声明类里面去invoke和赋值,不允许外面,甚至子类都能运用
event事件只能声明在类中,而委托可以声明在类外面
比如我们在cat类中定义一个事件
public event MiaoDelegate MiaoDelegateHandlerEvent;
public void MiaoNewEvent()
{
Console.WriteLine("{0} MiaoNewEvent", this.GetType().Name);
if (this.MiaoDelegateHandlerEvent != null)
{
this.MiaoDelegateHandlerEvent.Invoke();
}
}
然后只能在cat类中中进行invoke调用,如果我们定义一个子类继承cat类,如下:
public class ChildClass : Cat
{
public void Show()
{
this.MiaoDelegateHandlerEvent += null;
if (this.MiaoDelegateHandlerEvent != null)//子类也不能调用,调用报错
{
this.MiaoDelegateHandlerEvent.Invoke();
}
}
}
this.MiaoDelegateHandlerEvent.Invoke(); 这样是不能使用的,Invoke这个是完全不能调用的!
委托和事件的区别与联系?
委托是一个类型,比如Student
事件是委托类型的一个实例,加上了event的权限控制 比如学生加菲猫,是一个确切的实体
然后事件也可以通过下面来调用多个方法
{
Cat cat = new Cat();
//cat.MiaoDelegateHandler += new MiaoDelegate(new Mouse().Run);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Baby().Cry);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Mother().Wispher);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Brother().Turn);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Father().Roar);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Neighbor().Awake);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Stealer().Hide);
cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Dog().Wang);
cat.MiaoNewEvent();
Console.WriteLine("***************************");
}
下面写一个标准的的事件,事件一般分为三种:
1:事件的发布者:发布事件,并且在满足条件时候触发事件
2:事件的订户:关注事件,事件发生后,自己做出相应的动作
3:事件的订阅:把订户和发布者的事件关联起来
/// <summary>
/// 委托是一种类型,静态类不能被继承,所以委托不能声明静态,
/// event只是一个实例,所以可以生成实例
/// </summary>
class EventStandard
{
/// <summary>
/// 订阅:把订户和发布者的事件关联起来
/// </summary>
public static void Show()
{
iPhoneX phone = new iPhoneX()
{
Id = ,
Tag = "1.0"
};
// 订阅:把订户和发布者的事件关联起来
phone.DiscountHandler += new Student().Buy;
phone.DiscountHandler += new Teacher().Notice; phone.Price = ; }
/// <summary>
/// 订户:关注事件,事件发生后,自己做出对应的动作
/// </summary>
public class Student
{
public void Buy(object sender, EventArgs e)
{
iPhoneX phone = (iPhoneX)sender;
Console.WriteLine($"this is {phone.Tag} iphoneX");
XEventArgs args = (XEventArgs)e;
Console.WriteLine($"之前的价格{args.OldPrice}");
Console.WriteLine($"限制的价格{args.NewPrice}");
Console.WriteLine("立马买!!");
}
}
public class Teacher
{
public void Notice(object sender, EventArgs e)
{
iPhoneX phone = (iPhoneX)sender;
Console.WriteLine($"this is {phone.Tag} iphoneX");
XEventArgs args = (XEventArgs)e;
Console.WriteLine($"之前的价格{args.OldPrice}");
Console.WriteLine($"限制的价格{args.NewPrice}");
Console.WriteLine("立马买!!");
}
} /// <summary>
/// 事件参数 一般会为特定的事件去封装个参数
///订户:Teacher/Student:关注事件,事件发生后,自己做出对应的动作
/// </summary>
public class XEventArgs : EventArgs
{
public int OldPrice { set; get; }
public int NewPrice { set; get; }
} /// <summary>
/// 事件的发布者,发布事件,并且在满足条件时候触发事件
/// </summary>
public class iPhoneX
{
public int Id { set; get; }
public string Tag { set; get; }
public int Price
{
set
{
if (value < this._price)
{
this.DiscountHandler?.Invoke(this, new XEventArgs() { OldPrice = this._price, NewPrice = value });
this._price = value;
}
}
get { return this._price; }
} private int _price; /// <summary>
/// 打折事件
///
/// </summary>
public event EventHandler DiscountHandler;
}
}
EventHandler:框架自带,表示将用于处理不具有事件数据的事件的方法。EventHandler(object sender, EventArgs e)
下面这个例子也很经典
/// <summary>
/// 妈妈做好饭,触发爸爸和儿子一起吃饭,并且妈妈会把对应的菜单同时告知爸爸和儿子
/// </summary>
public class MyEvenStandard
{
public class Meal
{
public static void FinishMeal()
{
Mom mom = new Mom();
mom.EatHandler += (new Dad()).Eat;
mom.EatHandler += (new Son()).Eat;
mom.Cook();
}
} public class Mom
{
//EventHandler(object sender, EventArgs e) 事件的原型,里面sender则是发布者自己,e:是发布者的交互参数
public event EventHandler EatHandler;
public void Cook()
{
Console.WriteLine("开始吃饭了");
EatHandler?.Invoke(this, new MenuArags() { Fruid = "orange", Greens = "green pepper", Meat = "fish" });
}
}
/// <summary>
/// 爸爸
/// </summary>
public class Dad
{
public void Eat(object sender, EventArgs args)
{
var menArgs = (MenuArags)args;
Console.WriteLine($"儿子,我们今天晚上次:{menArgs.Greens},{menArgs.Fruid},{menArgs.Meat}");
}
} /// <summary>
/// 儿子
/// </summary>
public class Son
{
public void Eat(object sender, EventArgs args)
{
var menArgs = (MenuArags)args;
Console.WriteLine($"爸爸,我们今天晚上次:{menArgs.Greens},{menArgs.Fruid},{menArgs.Meat}");
}
} public class MenuArags : EventArgs
{
/// <summary>
/// 水果
/// </summary>
public string Fruid { set; get; } /// <summary>
/// 青菜
/// </summary>
public string Greens { set; get; } public string Meat { set; get; }
} }
C# 委托 事件的更多相关文章
- C# ~ 从 委托事件 到 观察者模式 - Observer
委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分: 参考 [1]. 初识事件 到 自定义事件: [2]. 从类型不安全的委托 到 类型安全的事件: [3]. 函数指针 ...
- C#委托,事件理解入门 (译稿)
原文地址:http://www.codeproject.com/Articles/4773/Events-and-Delegates-Simplified 引用翻译地址:http://www.cnbl ...
- 关于ios使用jquery的on,委托事件失效
$('.parents').on("click",'.child',function(){}); 类似上面这种,在ios上点击"child"元素不会起作用,解决 ...
- Observer设计模式中-委托事件-应用在消息在窗体上显示
Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...
- Asp.net用户控件和委托事件
在Asp.net系统制作过程中,门户类型的网站,我们可以用DIV+CSS+JS+Ajax全部搞定,但是一旦遇到界面元素比较复杂的时候,还是UserControl比较方便一些,各种封装,各种处理,然后拖 ...
- jQuery里面的普通绑定事件和on委托事件
以click事件为例: 普通绑定事件:$('.btn1').click(function(){}绑定 on绑定事件:$(document).on('click','.btn2',function(){ ...
- c#基础学习笔记-----------委托事件
这里有一个比较简单的委托实例应用(跨窗体操作控件) http://blog.csdn.net/bdstjk/article/details/7004035 还有一个比较详细的介绍并深入理解委托事件的讲 ...
- c#委托事件入门--第二讲:事件入门
上文 c#委托事件入门--第一讲:委托入门 中和大家介绍了委托,学习委托必不可少的就要说下事件.以下思明仍然从事件是什么.为什么用事件.怎么实现事件和总结介绍一下事件 1.事件是什么:. 1.1 NE ...
- Unity C#笔记 委托&事件
C#的委托与事件搭配,即是观察者模式的一种实现. 因为观察者模式的原理很易懂,不作多讲,本文纯粹用于记录语法. delegate(委托) //声明没有参数,没有返回值的委托类型XXXX public ...
- JavaScript中事件委托(事件代理)详解
在JavaScript的事件中,存在事件委托(事件代理),那么什么是事件委托呢? 事件委托在生活中的例子: 有三个同事预计会在周一收到快递.为签收快递,有两种办法:一是三个人在公司门口等快递:二是委托 ...
随机推荐
- python_redis简介与安装和使用
一.简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted ...
- spring bean 注入
概念 http://developer.51cto.com/art/200610/33311.htm http://kb.cnblogs.com/page/45266/ ==https://www.c ...
- 我的C#跨平台之旅(四):使用AOP(filter、attribute)进行系统增强
1.使用OData提速REST API开发 引入NuGet包:Microsoft.AspNet.WebApi.OData 在启动类中添加如下配置(示例为全局配置,也可基于Controller或Acti ...
- Java中的==与equals
当使用==操作符判断类时,实际上判断的是二者是否指向同一个对象 若要判断两个对象是否含有相同的数据,需使用Object类中的equals方法 java中所有类都是从Object类中派生出来的
- 不停止nginx服务,使配置文件生效
ps -ef | grep "nginx: master process" | grep -v "grep" | awk -F ' ' '{print $2}' ...
- Charles 学习笔记
之前接触过抓包工具Fiddler,一直在Windows下使用,感觉还是挺好用的.今天接触了Charles才有了了解,首先Charles设计的比较美丽,而且页面布局也是简单易学的,但这两款抓包工具本质的 ...
- 用apache和tomcat搭建集群,实现负载均衡
型的企业应用每天都需要承受巨大的访问量,在着巨大访问量的背后有数台服务器支撑着,如果一台服务器崩溃了,那么其他服务器可以使企业应用继续运行,用户对服务器的运作是透明化的,如何实现这种透明化呢?由如下问 ...
- DevOps最佳工具集实践
在列出DevOps 工具链之前,介绍一下什么是DevOps,虽然DevOps这个概念现在还没有标准的定义,但我们可以追溯一下其过去九年的历史发展过程(从2009年-2017年),列出几个相对明确又有所 ...
- 【设计经验】3、ISE中烧录QSPI Flash以及配置mcs文件的加载速度与传输位宽
一.软件与硬件平台 软件平台: 操作系统:Windows 7 64-bit 开发套件:ISE14.7 硬件平台: FPGA型号:XC6SLX45-CSG324 QSPI Flash型号:W25Q128 ...
- Akka-Cluster(5)- load-balancing with backoff-supervised stateless computation - 无状态任务集群节点均衡分配
分布式程序运算是一种水平扩展(scale-out)运算模式,其核心思想是能够充分利用服务器集群中每个服务器节点的计算资源,包括:CPU.内存.硬盘.IO总线等.首先对计算任务进行分割,然后把细分的任务 ...