C#事件(Event)学习日记
event 关键字的来由,为了简化自定义方法的构建来为委托调用列表增加和删除方法。
在编译器处理 event 关键字的时候,它会自动提供注册和注销方法以及任何必要的委托类型成员变量。
这些委托成员变量总是声明为私有的,因此不能直接从触发事件对象访问它们。
温馨提示:如果您对于委托不是很了解,您可以先看 C#委托(Delegate) ,这对您理解本章会有所帮助。
定义一个事件的步骤:
- 需要定义一个委托,它包含事件触发时将要调用方法
- 通过 event 关键字用相关委托声明这个事件
话不多说,我们来看一个示例:
1. 定义Car类:
public class Car
{
// 这个委托用来与Car事件协作
public delegate void CarEngineHandler(string msg); // 这种汽车可以发送这些事件
public event CarEngineHandler Exploded;
public event CarEngineHandler AboutToBlow; public int CurrentSpeed { get; set; } public int MaxSpeed { get; set; } public string PetName { get; set; } private bool CarIsDead; public Car()
{
MaxSpeed = ;
} public Car(string name, int maxSp, int currSp)
{
CurrentSpeed = currSp;
MaxSpeed = maxSp;
PetName = name;
} public void Accelerate(int delta)
{
// 如果Car无法使用了,触发Exploded事件
if (CarIsDead)
{
if (Exploded != null)
{
Exploded("sorry,this car is dead");
}
}
else
{
CurrentSpeed += delta; // 确认已无法使用,触发AboutToBlow事件
if ((MaxSpeed - CurrentSpeed) == && AboutToBlow != null)
{
AboutToBlow("careful buddy ! gonna blow !");
} if (CurrentSpeed >= MaxSpeed)
{
CarIsDead = true;
}
else
{
Console.WriteLine($"CurrentSpeed={CurrentSpeed}");
}
}
}
}
以上我们已经设定了Car对象发送两个自定义事件,这不再需要自定义注册函数,也不需要声明委托成员变量。稍后我们将说到如何使用这个汽车,在此之前,让我们了解一下事件的架构,揭开事件的神秘面纱。
2. 事件神秘面纱
C#事件事实上会扩展两个隐藏的公共方法,一个 add_事件名称,一个 remove_事件名称。
add_Exploded() CIL指令
remove_Exploded() CIL指令
代表事件本身的CIL代码使用 .addon 和 .removeon 指令调用对应的 add_xxx() 和 remove_xxx()方法
3. 使用Car类
了解了这些之后,我们来使用之前定义的Car类:
public class MyEvent
{
public static void Show()
{
WriteLine("fun with events");
Car c1 = new Car("bwm", , ); // 注册事件处理程序
c1.AboutToBlow += new Car.CarEngineHandler(CarIsAlomostDoomed);
c1.AboutToBlow += new Car.CarEngineHandler(CarAboutToBlow); Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded);
c1.Exploded += d; WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.Accelerate();
} // 注销,从调用列表中移除CarExploded()方法
c1.Exploded -= d; WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.Accelerate();
}
}
private static void CarExploded(string msg) => WriteLine($"CarExploded-> {msg}"); private static void CarAboutToBlow(string msg) => WriteLine($"CarAboutToBlow=>{msg}"); private static void CarIsAlomostDoomed(string msg) => WriteLine($"CarIsAlomostDoomed-> {msg}");
}
运行效果图:
为了进一步简化事件注册,我们可以用到委托章节学习到的方法组转换语法(解释:我可以在调用以委托作为参数的方法时,直接提供方法的名称,而不是委托对象)
下面请看使用方法组转换,注册和注销事件,粗体部分:
public static void Show()
{
WriteLine("fun with events");
Car c1 = new Car("bwm", , ); // 注册事件处理程序
c1.AboutToBlow += CarIsAlomostDoomed;
c1.AboutToBlow += CarAboutToBlow;
c1.Exploded += CarExploded;
WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.Accelerate();
}
// 注销,从调用列表中移除CarExploded()方法
c1.Exploded -= CarExploded;
WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.Accelerate();
}
}
4. 创建自定义事件参数
微软的事件模式:(System.Object sender,System.EventArgs args)这一两个参数的模型。
第一个参数 sender :表示一个对发送事件的对象(Car)的引用,
第二个参数 args :与该事件相关的信息
System.EventArgs 基类源代码:
public class EventArgs {
public static readonly EventArgs Empty = new EventArgs(); public EventArgs()
{
}
}
那么对于简单的事件类型来说,我们可以直接传递一个EventArgs的实例,但是如果我们期望传递自定义的数据,就应该从System.EventArgs派生出一个子类。
我们接下来就为我们的 Car 自定义一个符合这种事件模式的事件参数,新建一个 CarEventArgs 类,包含一个字符串,表示要发送给接收者的信息:
public class CarEventArgs : EventArgs
{
public readonly string msg;
public CarEventArgs(string message)
{
msg = message;
}
}
我们修改一下Car类,新添加一个 CarCustomEngineHandler 委托,并且更改相应的事件代码:
public class Car
{
public delegate void CarCustomEngineHandler(object sender, CarEventArgs e); // 模仿微软正规(object sender, EventArgs e)写法
public event CarCustomEngineHandler CustomExploded;
public event CarCustomEngineHandler CustomAboutToBlow; public void AccelerateCustom(int delta)
{
if (CarIsDead)
{
if (CustomExploded != null)
{
CustomExploded(this, new CarEventArgs("sorry,this car is dead"));
}
}
else
{
CurrentSpeed += delta; if ((MaxSpeed - CurrentSpeed) == && CustomAboutToBlow != null)
{
CustomAboutToBlow(this, new CarEventArgs("careful buddy ! gonna blow !"));
} if (CurrentSpeed >= MaxSpeed)
{
CarIsDead = true;
}
else
{
Console.WriteLine($"CurrentSpeed={CurrentSpeed}");
}
}
}
}
看一下调用粗体部分(是如何使用传递的参数sender,e的):
public class MyCustomEvents
{
public static void Show()
{
WriteLine("fun with events");
Car c1 = new Car("bwm", , ); c1.CustomAboutToBlow += CarIsAlomostDoomed;
c1.CustomAboutToBlow += CarAboutToBlow; Car.CarCustomEngineHandler d = CarExploded;
c1.CustomExploded += d; WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.AccelerateCustom();
} c1.CustomExploded -= d; WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.AccelerateCustom();
}
} private static void CarExploded(object sender, CarEventArgs e) => WriteLine($"CarExploded->{((Car)sender)?.PetName} {e.msg}"); private static void CarAboutToBlow(object sender, CarEventArgs e) => WriteLine($"CarAboutToBlow=>{((Car)sender)?.PetName} {e.msg}"); private static void CarIsAlomostDoomed(object sender, CarEventArgs e) => WriteLine($"CarIsAlomostDoomed->{((Car)sender)?.PetName} {e.msg}");
}
5. 泛型 EventHandler<T> 委托
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
由于很多自定义委托接受(object,EventArgs)这样的参数结构,那么我们可以使用框架内置的 EventHandler<> 来简化我们的事件 委托。
首先修改一下Car类:
public class Car
{
public event EventHandler<CarEventArgs> StandardExploded;
public event EventHandler<CarEventArgs> StandardAboutToBlow; public void AccelerateStandard(int delta)
{
if (CarIsDead)
{
if (StandardExploded != null)
{
StandardExploded(this, new CarEventArgs("sorry,this car is dead"));
}
}
else
{
CurrentSpeed += delta; if ((MaxSpeed - CurrentSpeed) == && StandardAboutToBlow != null)
{
StandardAboutToBlow(this, new CarEventArgs("careful buddy ! gonna blow !"));
} if (CurrentSpeed >= MaxSpeed)
{
CarIsDead = true;
}
else
{
Console.WriteLine($"CurrentSpeed={CurrentSpeed}");
}
}
}
}
调用代码其实和上一段并没有太大差异,这里还是贴出来:
public class MyStandardEvent
{
public static void Show()
{
WriteLine("fun with events");
Car c1 = new Car("bwm", , ); c1.StandardAboutToBlow += CarIsAlomostDoomed;
c1.StandardAboutToBlow += CarAboutToBlow; EventHandler<CarEventArgs> d = CarExploded;
c1.StandardExploded += d; WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.AccelerateStandard();
} c1.StandardExploded -= d; WriteLine("******Speeding up******");
for (int i = ; i < ; i++)
{
c1.AccelerateStandard();
}
} private static void CarExploded(object sender, CarEventArgs e) => WriteLine($"CarExploded->{((Car)sender)?.PetName} {e.msg}"); private static void CarAboutToBlow(object sender, CarEventArgs e) => WriteLine($"CarAboutToBlow=>{((Car)sender)?.PetName} {e.msg}"); private static void CarIsAlomostDoomed(object sender, CarEventArgs e) => WriteLine($"CarIsAlomostDoomed->{((Car)sender)?.PetName} {e.msg}");
}
6.匿名方法
这么简单的处理操作, CarExploded() ,CarAboutToBlow()这一的方法很少会被调用委托之外的任何程序所调用。从生成效率来说,手工定义一个由委托对象调用的方法有点麻烦耶。
为了解决这种情况,现在事件注册时,可以直接将一个委托与一段代码关联 -- 匿名方法。
我们修改一下调用Car类的地方(注意粗体部分、最后一个大括号 ";" 结束):
public class MyAnonymousMtehoden
{
public static void Show()
{
int aboutToBlowCounter = ; WriteLine("fun with events");
Car c1 = new Car("bwm", , ); c1.StandardAboutToBlow += delegate
{
WriteLine("Eek,going to fast");
}; c1.StandardAboutToBlow += delegate (object sender, CarEventArgs e)
{
aboutToBlowCounter++;
WriteLine($"CarAboutToBlow=>{((Car)sender)?.PetName} {e.msg}");
}; c1.StandardExploded += delegate (object sender, CarEventArgs e)
{
aboutToBlowCounter++; WriteLine($"Exploded=>{((Car)sender)?.PetName} {e.msg}");
}; for (int i = ; i < ; i++)
{
c1.AccelerateStandard();
} WriteLine($"aboutToBlowCounter={aboutToBlowCounter}");
}
}
本文参考《精通C#》
学无止境,望各位看官多多指教。
C#事件(Event)学习日记的更多相关文章
- 学习笔记---Javascript事件Event、IE浏览器下的拖拽效果
学习笔记---Javascript事件Event.IE浏览器下的拖拽效果 1. 关于event常用属性有returnValue(是否允许事件处理继续进行, false为停止继续操作).srcE ...
- Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群
Redis总结(五)缓存雪崩和缓存穿透等问题 前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...
- Lite OS学习之事件EVENT
1. Lite OS的事件EVENT,就是一个任务向另外一个任务通知事件的,不能数据传输.看下有的函数,实际比较复杂 2. 具体还是看编程,先全局结构体整个事件变量 /*事件控制结构体*/ EVENT ...
- libevent源码学习(9):事件event
目录在event之前需要知道的event_baseevent结构体创建/注册一个event向event_base中添加一个event设置event的优先级激活一个event删除一个event获取指定e ...
- android学习日记03--常用控件button/imagebutton
常用控件 控件是对数据和方法的封装.控件可以有自己的属性和方法.属性是控件数据的简单访问者.方法则是控件的一些简单而可见的功能.所有控件都是继承View类 介绍android原生提供几种常用的控件bu ...
- MySQL 定时器EVENT学习
原文:http://blog.csdn.net/lifuxiangcaohui/article/details/6583535 MySQL 定时器EVENT学习 MySQL从5.1开始支持event功 ...
- 经典线程同步 事件Event
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...
- android学习日记0--开发需要掌握的技能
一.开发android,我们需要哪些技能基础 1.Java基础知识 2.Linux基础知识 3.数据库基础知识 4.网络协议 5.Android基础知识 6.服务器端开发知识 1.Java基础知识 很 ...
- android学习日记03--常用控件Dialog
常用控件 9.Dialog 我们经常会需要在Android界面上弹出一些对话框,比如询问用户或者让用户选择.这些功能我们叫它Android Dialog对话框 对话框,要创建对话框之前首先要创建Bui ...
- 多线程面试题系列(6):经典线程同步 事件Event
上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的"线程所有权"特性所以关键段只能用于线程的互斥而不能用于同步.本篇介绍用事件Event来尝试解决这个线程同步问题.首先 ...
随机推荐
- SQL Server连接Oracle详细步骤
http://blog.csdn.net/weiwenhp/article/details/8093105 我们知道SQL Server和Oracle其实很多原理都类似.特别是一些常用的SQL语句都是 ...
- [ES7] Descorator: evaluated & call order
When multiple decorators apply to a single declaration, their evaluation is similar to function comp ...
- Android Drawable 和String 相互转化
在我们经常应用开发中,经常用到将drawable和string相互转化.注意这情况最好用于小图片入icon等. public synchronized Drawable byteToDrawable( ...
- JSP 内置对象的四种属性范围
在jsp页面中的对象,包括用户创建的对象(例如,javaBean对象)和JSP的隐含对象,都有一个范围属性.范围定义了在什么时间内,在哪一个JSP页面中可以访问这些对象.例如,session对象在会话 ...
- keepalive support-----Programming applications
TCP Keepalive HOWTO Prev Next 4. Programming applications This section deals with programming code ...
- JDBC Transaction Management Example---reference
In this post, we want to talk about JDBC Transactions and how we can manage the operations in a data ...
- C#绘制圆形时钟
本文由作者参考部分案例后加以修改完成: 参考链接如下: http://blog.csdn.net/xuemoyao/article/details/8001113 http://wenku.baidu ...
- 关于json中对象的删除
一个json对象在后台产生了,但是有些数据可能无效或者不合法,所以需要在前台作些例外处理,比如删除掉. json的删除有很多种,直接用过 delete json对象方式. 举例如下 Js代码 var ...
- BrainTree信用卡包
BrainTree是一个国外集成信用卡支付的卡包. 沙盒登陆地址: https://sandbox.braintreegateway.com/login 登陆沙盒得到商户ID.公钥.私钥. 1.配置w ...
- .Net之美读书系列(二):委托进阶
这次看书的知识点: 事件访问器 如果一个委托中注册了多个事件且需要获取其返回值的方法 委托的异常处理 委托处理超时的方法 异步委托 事件访问器 职能有: 1.对委托属性进行封装,不再直接该委托变量直接 ...