C#事件具体实现步骤

【菜科解读】
定义一个事件成员,表示该类型提供了如下功能:
1.能够在事件中注册方法 2.能够在事件中注销方法 3.当事件发生时,注册的方法会被通知
(事件内部维护了一个注册方法列表)
CLR的事件模型是基于委托的,它可以通过类型安全的方式调用回调方法。
而回调方法是订阅事件的对象接收通知的方式。
通过一个例子来说明:
①Fax对象的方法注册到MailManager事件 ②Pager对象的方法注册到MailManager事件 ③新的邮件到达MailManager ④MailManager对象向注册的方法发出通知,接收通知的方法可以随意处理。
具体实现步骤如下:
1.定义一个类型,能够hold住任何发送到事件通知接收者的信息
当一个事件被触发,触发事件的对象可能希望发送一些额外的信息给事件通知的接收对象。
这些额外的信息需要封装在它自己的类中,根据约定该类需要从System.EventArgs类派生,并且命名以EventArgs结尾。
这里定义一个NewMailEventArgs类:
public class NewMailEventArgs : EventArgs private readonly String m_from, m_to, m_subject; public NewMailEventArgs(String from, String to, String subject) m_from = from; m_to = to; m_subject = subject; public String From { get { return m_from; } } public String To { get { return m_to; } } public String Subject { get { return m_subject; } } }
关于EventArgs
[ComVisible(true)][Serializable]public class EventArgs public readonly static EventArgs Empty; static EventArgs() EventArgs.Empty = new EventArgs(); public EventArgs()}
这个类没有实际的用途,只是作为一个基类让其他对象继承。
很多对象不需要传递额外的信息,例如按钮事件,只是调用一个回调方法就够了。
当我们定义的事件不需要传递额外的信息时,这时调用EventArgs.Empty就行了,不需要重新构建一个EventArgs对象。
2.定义事件成员
public class MailManager { ... //NewMail事件名, //EventHanlder,所有的事件通知接收对象必须提供给该委托类型匹配的回调方法 public event EventHandler NewMail; }
System.EventHandler委托的定义为:public delegate void EventHandler(Object sender, TEventArgs e) where TEventArgs: EventArgs;
为什么这里第一个参数sender的类型是Object?毕竟MailManager类型是唯一触发这个事件的,所以可以设计成这样:void MethodName(MailManager sender,NewMailEventArgs e)这种情况会有一个弊端,当sender是SmtpMailManager时,回调方法也需要改变,使用Object能够很好的兼容。
定义回调方法的参数名约定为e,这样做主要是为了保持一致性。
方便开发人员。
事件机制要求所有的事件处理方法必须返回void,这是必要的,因为一个事件可能触发很多的回调方法,没有办法获取所有的返回值,索性就不允许返回值,全部为void。
有些FCL里面的事件处理程序没有遵循,而是返回了一个Assembly类型。
3.定义一个方法来响应事件的发生
按照惯例,这个类应该定义一个protected,virtual的方法供内部的代码调用。
这个方法接收一个NewMailEventArgs对象,这个对象包含要传递给消息接收方的一些信息。
如下:
protected virtual void OnNewMail(NewMailEventArgs e) { //复制一个委托的引用到临时字段temp,这样确保线程安全 EventHandler temp = Interlocked.CompareExchange(ref NewMail, null, null); //任何注册到事件里面的方法,通知它们 if (temp != null) { temp(this, e); } }
Tips:使用线程安全的方式触发事件(①——>④为不断改进的过程)
①当.NET第一次推出的时候,给开发者推荐的事件触发方式如下:
//v1.0protected virtual void OnNewMail(NewMailEventArgs e) { if (NewMail != null) { NewMail(this, e); } }
弊端:这里检查了NewMail不为null才触发,但是当检查完之后,在调用NewMail之前,有其他的线程从委托链中移除了一个委托,使得NewMail为null,此时会抛出异常。
②先将NewMail用一个临时变量存起来,这时就不会因为调用时被其他线程修改而抛出异常。
之所以能够这样做,是因为委托类型跟字符串类型一样是不可变的。
//v2.0protected void OnNewMail(NewMailEventArgs e) { EventHandler temp = NewMail; if (temp != null) { temp(this, e); } }
弊端:可能被编译器优化掉本地temp变量,如果发生这种情况,就回到了第一种了。
③修复上面的bug,如下:
//v3.0protected void OnNewMail(NewMailEventArgs e) { EventHandler temp = Thread.VolatileRead(ref NewMail); if (temp != null) { temp(this, e); } }
这里使用VolatileRead会强制读取temp的值,但是这里不能这样写,编译不通过。
但是有一个Interlocked.CompareExchange可以使用:
④
//v4.0 protected virtual void OnNewMail(NewMailEventArgs e) { //复制一个委托的引用到临时字段temp,这样确保线程安全 EventHandler temp = Interlocked.CompareExchange(ref NewMail, null, null); //任何注册到事件里面的方法,通知它们 if (temp != null) { temp(this, e); } }
如果NewMail为null,CompareExchange将NewMail的值改变为null,如果不为null则返回原值。
换句话说,CompareExchange不会改变NewMail的值,只是以线程安全的方式返回NewMail的值,这里是一个原子操作。
第④个版本是最佳的,技术上最正确的版本。
实际开发中还是可以使用第②个版本,因为JIT编译器能够识别这种模式而不去优化本地的temp变量。
特别地,所有微软的JIT编译器都遵循不会对堆引入新的读取,因此缓存一个引用在本地变量可以确保堆引用只被访问一次(这是没有写入文档的,理论上,还是可能发生变化,所以最好选用第④版本。
)
为了方便可以定义一个扩展方法来封装:
public static class EventArgExtensions { public static void Raise(this TEventArgs e, Object sender, ref EventHandler eventDelegate) where TEventArgs : EventArgs { EventHandler temp = Interlocked.CompareExchange(ref eventDelegate, null, null); if (temp != null) { temp(sender, e); } } }
然后可以重写OnNewMail:
protected virtual void OnNewMail(NewMailEventArgs e) e.Raise(this, ref NewMail); }
4.定义一个方法用来传递一些输入到事件
public void SimulateNewMail(String from, String to, String subject) NewMailEventArgs e = new NewMailEventArgs(from, to, subject); OnNewMail(e); }123在本页阅读全文 本文导航 第1页: 首页 第2页: 编译器是怎么实现事件的? 第3页: 定义类型监听事件 事件,具体,实现,步骤,定义,一个,事件,成员,
缴纳社保费的比例具体是多少
医疗保险:单位缴费比例在6.5%-10%之间,个人缴费比例为2%加上固定的3元。
失业保险:单位缴费比例在1%-2%之间,个人缴费比例为0.2%-1%。
工伤保险:单位缴费比例根据行业范围确定,在0.5%-2%之间,个人不缴费。
生育保险:单位缴费比例为0.8%-0.6%,个人不缴费.公积金:单位和个人缴费比例根据企业实际情况选择,原则上最高缴费额不得超过当地职工平均工资的10%-300%的12%。
需要注意的是,各地的具体缴费比例可能会有所不同,以上信息仅供参考。
(注:本文数据仅供参考,具体以当地缴费标准为准)
天津跨省要如何转社保,异地转社保的具体流程是什么
本文将为您介绍转移的必要性和具体办理方法。
下面随新社通小编一起了解详情。
天津跨省要如何转社保?需要什么资料?社保跨省如何进行转移(天津社保转移办理介绍)第一,天津社保异地转移流程:天津社保怎么跨省转移?社保卡转移手续是需要在转入地社保局申请开具《调出证明或函》,然后凭本人身份证,养老保险本本材料在调出社保局申请即可。
天津跨省办理流程45个工作日办完对于参保人员跨省流动就业的,转移养老保险关系需要走三个流程,新参保地审核转移接续申请并向原参保地发出同意接受函——原参保地办理转移手续——新参保地接受转移手续和资金,三个流程走完之后即可办妥转移接续手续,每个流程最多15个工作日,也就是说对于参保者来说,最多45个工作日就可以将全部手续办完。
有关负责人表示,转移手续极大地方便了参保人,参保人只要申请即可,剩下的工作将由两地社保部门进行对接转移。
天津社保转移办理流程参保人员在新就业地建立基本养老保险关系和缴费后,由用人单位或参保人员向新参保地社保经办机构提出基本养老保险关系转移接续的书面申请。
新参保地社保经办机构在15个工作日内,审核转移接续申请,对符合条件的,向参保人员原基本养老保险关系所在地的社保经办机构发出同意接收函,并提供相关信息;对不符合转移接续条件的,向申请单位或参保人员作出书面说明。
原基本养老保险关系所在地社保经办机构在接到同意接收函的15个工作日内,办理好转移接续的各项手续。
新参保地经办机构在收到参保人员原基本养老保险关系所在地社保经办机构转移的基本养老保险关系和资金后,应在15个工作日内办结有关手续,并将确认情况及时告诉用人单位或参保人员。
天津社保卡跨省转移的实施为人们提供了方便,相关人士有需要可以进行办理。
第二,天津社保转移所需材料如下:《解除劳动合同证明》。
本人身份证、户口本原件、复印件。
养老保险手册。
如本人不能亲自办理,则需递交《委托书》,注明本人与被委托人的关系及委托人和被委托人的身份证等自然情况,要求本人签字、按手印。
异地社保部门出具的养老保险参保证明或就业《劳动合同》。
被委托人的身份证原件、复印件。
本人《基本养老保险关系转移接续申请》。