深刻理解:C#中的委托、事件

 

C#中的事件还真是有点绕啊,以前用JavaScript的我,理解起来还真是废了好大劲!刚开始还真有点想不明白为什么这么绕,想想和JS的区别,最后终于恍然大悟!

C#中事件绕的根本原因:

  1. C#的方法,它不是一个类型,它只是其它类型的成员;

  2. C#是一个强类型的语言,定义方法时,它的参数必须指定类型,如public void add(int n){...};

所以,一个方法不能直接作为其它方法的参数,把一个方法名作为参数,无法指定类型啊,会报错!那我就想啊,既然不能直接传入,那我传入整个对象总可以吧,通过传进来的对象来执行该方法,如下代码:

using System;namespace MyEventTest{   

 public class SomeClass{      
  public void Start(int a)

{ Console.WriteLine("Go:{0}",a); }}  
  
    public class Publisher{      
          public void StartEvent(int a, SomeClass sc)        {            if (sc != null){sc.Start(a);  //触发回调方法}}}  
   
    public class MainClass{        static void Main()        {SomeClass some = new SomeClass();Publisher p = new Publisher();p.StartEvent(5,some); //Go:5}} }

以上方法确实可以,但C#不完全是这样实现事件的,因为方法的特殊性,C#引入了委托的概念,让委托对象来代表方法作为其它方法的参数;而事件对象,其实就是一个委托对象。下面先介绍一下委托:

委托

对应于以上方法:
public void Start(int a) { Console.WriteLine("Go:{0}",a); }
我们可以定义一个委托类型:
public delegate void MyDel(int a);

委托的定义:

  1. 委托MyDel它是一个类型,类型名就是MyDel;定义委托相当于定义一个新类,委托在后台实现为派生自System.Delegate类。

  2. 定义委托,就是告诉编译器该委托将表示哪种方法(返回值类型+方法签名),该方法可以是任意类型的实例方法、静态方法,只要方法的签名、返回值类型与委托匹配,那么该委托的实例就可以引用这些方法。

  3. 使用委托,必须创建该委托的实例,并为它指定要引用的方法,如:MyDel d = some.Start;注意这里不是some.Start()

  4. 委托对象支持"+","+="来为它添加更多的方法引用,而"-","-="则是删除引用;

  5. 引用了多个方法的委托就叫多播委托,多播委托派生自基类System.MulticastDelegate类,它是System.Delegate类的子类

  6. 注意:只要委托对象还存在对方法的引用,它就一直占用内存哦!我想可以用d = null;来释放委托对象d;

  7. 可以对委托对象执行调用,如:d(5);它将把调用传递给它所引用的方法some.Start(5);,对于多播委托,它将按顺序调用它引用的所有方法,但如果其中一个方法抛出异常,且没在方法内部处理,则将会将异常往外抛出,之后的方法调用将终止。

    使用委托的规则:

  8. 委托是和类一个级别的,可以在能定义类的任何地方定义委托;

  9. 委托不支持继承;

  10. 可以为委托类型定义任意常见的访问修饰符;

  11. 委托对象所引用的方法也可以是匿名方法、Lambda 表达式;

  12. 多播委托的返回值类型必须是void,否则就只能得到委托调用的最后一个方法的结果。

  13. 在.NET 4.0中,委托开始支持协变与逆变,这样一来,定义委托类型时的签名可以和所要引用的方法的签名不完全匹配(不同类型之间必须是派生关系)

  14. 委托支持泛型,.NET预定义了两个泛型版本的委托:

    • Action< T >委托表示引用一个返回值类型为void的方法,根据参数个数存在不同的变体版本;如:Action<in T1, in T2>

    • Func< T >委托表示引用一个带返回值类型的方法,根据参数个数存在不同的变体版本;如:Func<in T1, out TResult>1个参数T1和返回值类型TResult。

事件

说完了委托的概念,就可以继续讲事件了,因为事件是基于委托的!

事件的概念:

  • 类或对象可以通过事件向其他类或对象通知发生的相关事情。

  • 发送事件的类称为“发行者”,接收事件的类称为“订阅者”。(就是设计模式中的订阅发布者模式);

  • 一个事件可以有多个订阅者。 一个订阅者可处理来自多个发行者的多个事件。如果一个事件有多个订阅者,当引发该事件时,会同步调用多个事件处理程序。也可异步调用。

.NET Framework 类库中的所有事件均基于 EventHandler 委托,还有泛型版本EventHandler<EventArgs>,这个委托是.NET预定义的,不需要我们定义,可以直接用它来实例化一个事件对象,定义如下:

参数object sender对象是对发布者的实例的引用,EventArgs e对象主要用来存储事件数据

public delegate void EventHandler(object sender, EventArgs e); //EventArgs主要用来存储事件数据

public delegate void EventHandler<TEventArgs>(object sender, EventArgs e);

虽然在自定义的类中的事件可基于任何有效委托类型,但是,通常建议使用.NET预定义事件委托类型让事件基于 .NET 标准事件模式

下面是我总结的发布基于 .NET 标准事件模式的4个步骤:

第1步:在发布者类中实例化委托事件,并定义一个实例方法,用来调用委托事件(因为委托事件只能通过定义它的类的实例来调用)。

定义发布者类之前可先定义一个用来存储事件数据的类(它必须派生于EventArgs基类),如下:

注意:在方法StartEvent()中,声明了一个变量,来保存事件对象的副本,这样在取得事件对象的副本后,到触发事件时,这段时间内,这个事件副本就不会受其它线程的影响。如:在此期间,其它线程注销了回调方法,那么MyEvent就为null了,这时再触发事件将引发错误。(这就是线程安全的事件,当然还可以通过锁机制,或者为事件对象始终引用一个空方法)

public class MyEventArgs: EventArgs  //定义存储事件数据的类{  
 public int Current{get;set;} }
 
 public class Publisher{  
 public event EventHandler<MyEventArgs> MyEvent; //第1步:实例化委托事件public int Sum{get;set;}
   public void StartEvent(int a)      {      
       var EventCopy = MyEvent; //每次都取一个副本MyEventArgs args = new MyEventArgs();args.Current = a;    
            this.Sum += a;          if (EventCopy != null){EventCopy(this,args);  //调用事件}} }

第2步:定义订阅者类,在该类中定义和委托事件相匹配的方法(事件触发时,实际要执行的方法)

public class Subscriber{    

public
void Dosomething1(object obj, MyEventArgs e)    
{Publisher p = (Publisher)obj;Console.WriteLine("Meg: Sum = {0}, Current = {1}", p.sum, e.Current);}     public void Dosomething2(object obj, MyEventArgs e)     {} }

第3步:在客户端代码中,在发布者类的实例上为委托事件注册回调方法

public class MainClass
{static void Main(){Publisher p = new Publisher{ Sum = 0 };Subscriber sub = new Subscriber();p.MyEvent += sub. Dosomething1;  //注册回调方法p.MyEvent += sub. Dosomething2;p. StartEvent( 5 ); //调用方法,间接触发事件p.MyEvent -= sub. Dosomething1;  //取消注册}
}

要点:事件对象其实就是一个委托对象,把事件当委托来看,就比较容易理解了!不要被Event这个单词给蒙蔽了!

介绍完了!下回将介绍C#中的其它一些较难理解的概念!

原文地址:http://www.cnblogs.com/susufufu/p/6160764.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325512.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mybatis中,collection配置后查询只显示一条记录

描述一下问题&#xff1a; 已知有两个表&#xff0c;一个是user表&#xff0c;一个是address,一&#xff08;user&#xff09;对多(address)的关系&#xff0c;在user的实体类里面写属性&#xff1a; private List<Address> addressList new ArrayList<Address>(…

Java中的List你真的会用吗

转载自 Java中的List你真的会用吗 List是Java中比较常用的集合类&#xff0c;关于List接口有很多实现类&#xff0c;本文就来简单介绍下其中几个重点的实现ArrayList、LinkedList和Vector之间的关系和区别。 List List 是一个接口&#xff0c;它继承于Collection的接口。它…

Android 全局字体设置 例如楷体

1、在res下新建资源文件目录font&#xff0c;把字体文件拷贝到font文件夹中 2、在AndroidManifest.xml中的application节点下&#xff0c;设置全局style&#xff0c;引入字体文件 <item name"android:fontFamily">font/pingfang_sc_regular</item>或者

.Net Core中使用ref和Spanamp;lt;Tamp;gt;提高程序性能

一、前言 其实说到ref&#xff0c;很多同学对它已经有所了解&#xff0c;ref是C# 7.0的一个语言特性&#xff0c;它为开发人员提供了返回本地变量引用和值引用的机制。Span 也是建立在ref语法基础上的一个复杂的数据类型&#xff0c;在文章的后半部分&#xff0c;我会有一个例…

微服务为什么选Spring Cloud

转载自 微服务为什么选Spring Cloud 现如今微服务架构十分流行&#xff0c;而采用微服务构建系统也会带来更清晰的业务划分和可扩展性。同时&#xff0c;支持微服务的技术栈也是多种多样的&#xff0c;本系列文章主要介绍这些技术中的翘楚——Spring Cloud。这是序篇&#x…

压力与动力是否成正比?

昨天在班里测试了下&#xff0c;检测他们数据库学的怎么样&#xff0c;看他们平时在课堂上的互动挺棒。看了下题&#xff0c;不是很难&#xff0c;满怀着愉悦的心情去打印了50份&#xff0c;挨个分发下去&#xff0c;由于我18级那边有课要上&#xff0c;所以这边的考试就辛苦王…

[开源] 基于ABP,Hangfire的开源Sharepoint文件同步解决方案----SuperRocket.SPSync

&#xff08;一&#xff09;项目背景 Sharepoint是微软的一个产品&#xff0c;很多公司都在使用它&#xff0c;也有很多公司以前使用它&#xff0c;现在可能需要移植到别的平台&#xff0c;也可能只是移植其中的文件存储&#xff0c;比如说移植到微软云&#xff0c;或者亚马逊云…

永远不要、不要、不要、不要放弃

Never, never, never, never give up. 永远不要、不要、不要、不要放弃。今天来写一下18级学生们的状态吧&#xff0c;最近主要是解决了1班的三大问题&#xff0c;第一&#xff0c;上机测试问题。第二&#xff0c;周一到四期间学习任务安排问题。第三&#xff0c;学习氛围的进一…

JS的时间定时器

<script>var t null;t setTimeout(time, 1000); //開始运行function time() {clearTimeout(t); //清除定时器dt new Date();var y dt.getFullYear();var mt dt.getMonth() 1;var day dt.getDate();var h dt.getHours(); //获取时var m dt.getMinutes(); //获取分…

微软正式发布XAML Standard与.NET Standard 2.0:现已提供下载

微软在本月早些时候召开的 Build 2017 开发者大会上的披露的 XAML Standard 和 .NET Standard 2.0&#xff0c;现已正式发布。新工具旨在为开发者们带来“基于同一标准的跨平台 XAML 语言结构”&#xff08;基于 UWP 和 Xamarin.Forms&#xff09;&#xff0c;以及基于社区反馈…

Spring Boot 2.X 来临,本文将带你起飞

转载自 Spring Boot 2.X 来临&#xff0c;本文将带你起飞 当前互联网技术盛行&#xff0c;以Spring 框架为主导的Java 互联网技术成了主流&#xff0c;而基于Spring 技术衍生出来的Spring Boot&#xff0c;采用了“约定优于配置”的思想&#xff0c;极大地简化了Spring 框架…

时间胶囊——给未来的留言板

时间胶囊”是一个给未来的留言板&#xff0c;你可以为自己&#xff0c;朋友&#xff0c;爱人&#xff0c;家人&#xff0c;或者任何人留下你现在想对他们说的话、图片&#xff0c;将来某一天&#xff0c;他们将来这里打开“时间胶囊”读到你的留言&#xff01;那么“时间胶囊”…

Echarts五步法加初体验

使用步骤&#xff1a; 引入echarts 插件文件到html页面中准备一个具备大小的DOM容器 <div id"main" style"width: 600px;height:400px;"></div>初始化echarts实例对象 var myChart echarts.init(document.getElementById(main));指定配置项…

深入理解C#:编程技巧总结(一)

以下总结参阅了&#xff1a;MSDN文档、《C#高级编程》、《C#本质论》、前辈们的博客等资料&#xff0c;如有不正确的地方&#xff0c;请帮忙及时指出&#xff01;以免误导&#xff01; 1..实现多态性的两种方式&#xff1a;继承抽象类、实现接口 其实就是协变的应用&#xff…

使用中控指纹采集器开发指纹识别案例V1.0

这两天正好有点琐碎的时间&#xff0c;就将两年前未开发完毕的指纹识别项目翻出来继续写了写。 运行环境&#xff1a;  中控指纹采集器  Win10操作系统  .netframework4.0  Sqlserver2008及以上 源码已经上传到微信公众号【雄雄的小课堂】中&#xff0c;回复“指纹识别…

在CentOS上使用Jexus托管运行 ZKEACMS

ZKEACMS Core 是基于 .net core 开发的&#xff0c;可以在 windows, linux, mac 上跨平台运行&#xff0c;接下来我们来看看如何在 CentOS 上使用Jexus托管运行 ZKEACMS&#xff0c;通常我们在Linux部署ASP.NET Core应用&#xff0c;按照微软的官方文档&#xff0c;我们通常需要…

中控指纹采集器开发指纹识别项目(说明)

历史指纹识别相关开发版本&#xff1a;指纹识别开发1.0&#xff0c;开发时间&#xff1a;2018-01-04 指纹识别开发2.0&#xff0c;开发时间&#xff1a;2018-01-04指纹识别开发3.0&#xff0c;开发时间&#xff1a;2020-01-06可以从时间上看的出来&#xff0c;在2018年1月4日&a…

MSSQL-Scripter,一个新的生成T-SQL脚本的SQL Server命令行工具

这里向大家介绍一个新的生成T-SQL脚本的SQL Server命令行工具&#xff1a;mssql-scripter。它支持在SQL Server、Azure SQL DB以及Azure SQL DW中为数据库生成CREATE和INSERT T-SQL脚本。 Mssql-scripter是一个跨平台的命令行工具&#xff0c;功能等同于SQL Server Management…

用startSmoothScroll实现RecyclerView滚动到指定位置并置顶,含有动画。

RecyclerView滚动到指定位置并置顶 RecyclerView本身提供了几个定位的方法&#xff0c;除了手动滑动的scrollTo&#xff0c;smootScrollTo和scrollBy&#xff0c;smoothScrollBy方法之外&#xff0c;有一个直接滑动到指定位置item的scrollToPosition方法和另一个在此基础上平滑…

重要说明,粉丝必看【java人脸识别说明】

重要通知关于人脸识别简要说明&#xff1a; 源码即日起由免费改为收费。以下是微信收款码&#xff0c;如果有需要可以直接扫码转账即可。&#xff08;注意&#xff0c;源码均为测试好的&#xff0c;故各位在开发的过程中遇到的源码问题不提供任何技术支持。【转账完成之后可以直…