C# 事件
1.事件概述
事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。
C# 中使用事件机制实现线程间的通信。
事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。
发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
2.使用事件有6个步骤:
事件处理程序:由订阅器注册到事件的方法,在发布器触发事件时执行。事件处理程序方法可以定义在事件所在的类或结构中,也可以定义在不同的类或结构中。
(1)声明委托:事件和事件处理程序必须有共同的签名和返回类型,它们通过委托类型进行描述。
(2)声明事件:发布器类必须声明一个订阅器类可以注册的事件成员。当声明的事件为public 时,称为发布了事件。
(3)触发事件:发布器类中"触发"事件并导致调用注册的所有事件处理程序的代码。
(4)声明事件处理程序:事件处理程序必须具有与事件的委托相同的返回类型和签名。
(5)订阅事件并添加或删除事件处理程序:订阅者必须订阅事件才能在它被触发时得到通知。
(6)执行触发事件语句。
注意点:声明委托、声明事件、触发事件一般放在发布器类中;
声明事件处理程序一般放在订阅器类中;
订阅事件并添加或删除事件处理程序可以放在主函数中也可以放在订阅器类中
执行触发事件语句一般放在主函数中
3.声明事件:
1.声明事件代码
csharp
class Incrementer
{
//event 关键字
//EventHandler 是委托类型(委托类型是什么,这里就是什么),.Net框架提供事件使用的标准模式就是System命名空间声明的EventHandler委托类型(后面会提到)
//CountedADozen 事件名
public event EventHandler CountedADozen;
}
可以使用逗号分隔,同时声明多个事件
csharp
public event EventHandler myEvent1,myEvent2,myEvent3;
2.注意事项
(1)事件声明首先需要声明委托(如果使用默认的EventHandler委托类型,就不需要声明委托了),因为声明事件需要使用到委托类型,任何附加到事件的处理程序都必须与委托类型的签名和返回类型匹配。
(2)事件必须声明在一个类中,事件是类或结构的成员,事件成员被隐式自动化为null
(3)声明为public,这样其他类和结构可以在它上面注册事件处理程序。
(4)不能使用对象创建表达式(new 表达式)来创建它的对象。
4.订阅事件:
订阅者向事件添加事件处理程序,事件处理程序必须具有与事件的委托相同的返回类型和签名。
使用+=运算符来为事件增加事件处理程序。
事件处理程序的规范可以是以下任意一种:
实例方法的名称;静态方法的名称;委托形式;匿名方法;Lambda表达式
例如:
csharp
//incrementer这个对象是发布器类的对象
//CountedADozen 是事件名
//IncrementDozensCount 是实例方法
incrementer.CountedADozen+=IncrementDozensCount;//使用方法引用形式的实例方法
incrementer.CountedADozen+=ClassB.CounterHandlerB;//使用方法引用形式的静态方法
incrementer.CountedADozen+=new EventHandler (cc.CounterHandlerC);//委托形式
incrementer.CountedADozen+=()=>DozensCount++;//Lambda表达式
incrementer.CountedADozen+=delegate{DozensCount++;}//匿名方法
5.触发事件:
在触发事件之前和null进行比较,从而查看是否包含事件处理程序,如果事件是null,则表示没有,不能执行。
触发事件语法和调用方法一样:使用事件名称,后面跟的参数列表包含在圆括号中;参数列表必须与事件的委托类型相匹配
csharp
if(CountedADozen !=null)//确认有方法可以执行
{
//CountedADozen 事件名
CountedADozen (source,args);//触发事件
}
事件声明和触发事件的代码放在发布器类中:
6.完整代码示例:
完整代码展示如下:发布器类Incrementer和订阅器类Dozens,在构造函数中,Dozens类订阅事件,将Incrementer_CountedADozen作为事件处理程序;在Incrementer类的DoCount方法中,每增长5个数就触发CountedADozen事件。
发布器类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo8_0727
{
//1.声明委托
delegate void Handler();
//发布器类
internal class Incrementer
{
public event Handler CountedADozen;//2.声明事件
public void DoCount()
{
for (int i = 0; i < 100; i++)
{
if (i%5==0&&CountedADozen!=null) //判断条件
{
CountedADozen();//3.触发事件
}
}
}
}
}
订阅器类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo8_0727
{
internal class Dozens
{
public int DozensCount { get; private set; }
public Dozens(Incrementer incrementer )
{
DozensCount = 0;
//5.订阅事件并添加事件处理程序
incrementer.CountedADozen += Incrementer_CountedADozen;
}
private void Incrementer_CountedADozen()//4.声明事件处理程序
{
DozensCount++;
}
}
}
主函数类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo8_0727
{
internal class Program
{
static void Main(string[] args)
{
Incrementer incrementer = new Incrementer();
Dozens dozens = new Dozens(incrementer);
//6.执行触发事件语句
incrementer.DoCount();
Console.WriteLine("Number of dozens={0}",dozens.DozensCount);
Console.ReadKey();
}
}
}
7.标准事件用法
.Net框架提供了事件使用的标准模式,.NET 类库中的所有事件均基于 EventHandler 委托,定义如下:
csharp
public delegate void EventHandler(object sender, EventArgs e);
//第一个参数用来保存触发事件的对象的引用
//第二个参数用来保存状态信息,指明什么类型适用于该应用程序:
无需声明该委托,因为它已在创建 C# 项目时包括的 System 命名空间中声明。
发布器类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo9_0727
{
internal class Incrementer
{
//声明事件
public event EventHandler CountedADozen;
public void DoCount()
{
for (int i = 0; i < 100; i++)
{
if (i%5==0&&CountedADozen!=null)
{
//触发事件
CountedADozen(this,null);
}
}
}
}
}
订阅器类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo9_0727
{
internal class Dozens
{
public int DozensCount { get; private set; }
public Dozens(Incrementer incrementer)
{
DozensCount = 0;
//订阅事件并添加事件处理程序
incrementer.CountedADozen += Incrementer_CountedADozen;
}
//声明事件处理程序
private void Incrementer_CountedADozen(object sender, EventArgs e)
{
DozensCount++;
}
}
}
主函数类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo9_0727
{
internal class Program
{
static void Main(string[] args)
{
Incrementer incrementer = new Incrementer();
Dozens dozens = new Dozens(incrementer);
//执行触发事件语句
incrementer.DoCount();
Console.WriteLine("Number of dozens={0}",dozens.DozensCount);
Console.ReadKey();
}
}
}
8.通过扩展EventArgs来传递数据
如果希望传递数据,必须声明一个派生自EventArgs 的类,使用合适的数据来保存需要的数据。
为了向自己的事件处理程序的第二个参数传入数据,需要声明一个派生自EventArgs的自定义类,它可以保存我们需要传入的数据。类的名称应该以EventArgs结尾。
要获得该类,需要使用泛型委托。(后续会提到)
代码演示:
自定义派生类IncrementerEventArgs
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo5_0726
{
//自定义派生类继承EventArgs类
internal class IncrementerEventArgs: EventArgs
{
public int IterationCount { get; set; }//存储一个整数
}
}
发布器类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo5_0726
{
internal class Incrementer1
{
// 声明事件
public event EventHandler<IncrementerEventArgs> CountedADozen;
public void DoCount()
{
IncrementerEventArgs args=new IncrementerEventArgs();
for (int i = 0; i < 100; i++)
{
if (i % 5 == 0 && CountedADozen != null)
{
args.IterationCount = i;
CountedADozen(this, args);//在触发事件时传递参数
}
}
}
}
}
订阅器类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo5_0726
{
internal class Dozens1
{
//int _DozensCount;
public int DozensCount
{
get;
private set;
}
public Dozens1(Incrementer1 incrementer1)
{
DozensCount = 0;
incrementer1.CountedADozen += Incrementer1_CountedADozen;
}
private void Incrementer1_CountedADozen(object sender, IncrementerEventArgs e)
{
Console.WriteLine("Incremented at iteration:{0} in {1}",e.IterationCount,sender.ToString());
DozensCount++;
}
}
}
主类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace demo5_0726
{
internal class Program
{
static void Main(string[] args)
{
Incrementer1 incrementer1 = new Incrementer1();
Dozens1 dozens1 = new Dozens1(incrementer1);
incrementer1.DoCount();
Console.WriteLine("Number of dozens={0}",dozens1.DozensCount);
Console.ReadKey();
}
}
}