C# 和 .NET 是支持事件驱动编程的,但它的处理需要委托实现来调度事件,所以事件和委托是紧密关联(耦合性高)。
事件是允许类或对象的某些状态发生改变时通知其它类或对象。
.NET 中的事件遵循观察者设计模式,常用的设计模式不熟悉的可以网上查查。
发送或引发事件的类称为发布者,接收或处理事件的类称为订阅者。
声明事件步骤
要在类中声明事件,首先必须声明事件的委托类型,如下:
public delegate void Notify();
其次,声明事件,如下:
public event Notify ProcessCompleted;
第三,调用事件,如下:
if(ProcessCompleted != null)
ProcessCompleted();
//或者
//ProcessCompeleted?.Invoke();
只能在声明事件的类中调用事件。
第四,注册事件,如下:
+= Sample_ProcessCompeleted;
如果是要取消事件,如下:
-= Sample_ProcessCompeleted;
完整代码:
public class EventSample
{
public delegate void Notify();
public event Notify ProcessCompeleted;
public void Start()
{
("开始作业");
//业务逻辑
//处理完毕后触发事件
OnProcessCompeleted();
}
protected virtual void OnProcessCompeleted()
{
ProcessCompeleted?.Invoke();
//判断是否订阅了
//if (ProcessCompeleted != null)
// ProcessCompeleted();
}
}
class Program
{
static void Main(string[] args)
{
EventSample sample = new EventSample();
+= Sample_ProcessCompeleted;
();
();
}
private static void Sample_ProcessCompeleted()
{
("处理完成");
}
}
上面,Start() 方法最后一行调用了 OnProcessCompeleted() ,这引发了一个事件,通常,要引发事件,应使用名称定义受保护的虚拟方法 On<EventName> 。protected 和 virtual 使派生类能够覆盖引发事件的逻辑。但是,派生类应始终调用 On<EventName> 基类方法,以确保注册的委托接收事件。
OnProcessCompleted()方法调用委托 ProcessCompleted?.Invoke();。这会调用事件中注册的所有事件处理程序方法。
Program 类是 ProcessCompeleted 事件的订阅者。它使用 += 运算符向事件注册。Sample_ProcessCompeleted() 是处理事件方法且与 Notify 委托声明的一致。
内置 EventHandler 委托
.NET 还内置了两个常用的委托类型 EventHandler 和 EventHandler<TEventArgs> 。通常,任何事件都应包括两个参数:事件源和事件数据。EventHandler 主要用于不包含事件数据;对包含事件数据的使用 EventHandler<TEventArgs> 。
先看 EventHandler 的使用。
public class EventSample
{
//public delegate void Notify();
public event EventHandler ProcessCompeleted;
public void Start()
{
("开始作业");
//业务逻辑
//处理完毕后触发事件
OnProcessCompeleted();
}
protected virtual void OnProcessCompeleted(EventArgs e)
{
ProcessCompeleted?.Invoke(this, e);
}
}
class Program
{
static void Main(string[] args)
{
EventSample sample = new EventSample();
+= Sample_ProcessCompeleted;
();
();
}
private static void Sample_ProcessCompeleted(object sender,EventArgs e)
{
("处理完成");
}
}
上面代码中,Sample_ProcessCompeleted() 方法多了两个与 EventHandler 匹配的参数。此外,当我们在方法中使用引发事件时,this 作为发送者传递。另外,我们的事件不需要数据,所以使用 。
传递事件数据
大多数事件都会向订阅者发送一些数据。EventArgs 类是所有事件数据的基类。.NET 还包含了其它的内置事件数据类,如:SerialDataReceivedEventArgs 。它遵循以 EventArgs 结尾的命名方式。我们可以通过继承 EventArgs 来自定义事件数据。
public class ProcessCompeletedEventArgs : EventArgs
{
public bool IsCompeleted { get; set; }
/// <summary>
/// 完成时间
/// </summary>
public DateTime CompeletedAt { get; set; }
}
public class EventSample
{
//public delegate void Notify();
public event EventHandler<ProcessCompeletedEventArgs> ProcessCompeleted;
public void Start()
{
("开始作业");
//业务逻辑
ProcessCompeletedEventArgs e = new ProcessCompeletedEventArgs
{
IsCompeleted = true,
CompeletedAt =
};
//处理完毕后触发事件
OnProcessCompeleted(e);
}
protected virtual void OnProcessCompeleted(ProcessCompeletedEventArgs e)
{
ProcessCompeleted?.Invoke(this, e);
}
}
class Program
{
static void Main(string[] args)
{
EventSample sample = new EventSample();
+= Sample_ProcessCompeleted;
();
();
}
private static void Sample_ProcessCompeleted(object sender, ProcessCompeletedEventArgs e)
{
("IsCompeleted:{0},CompletedAt:{1}", , );
("处理完成");
}
}
总结
我们总结下事件的一些要点:
-
事件是委托的封装,以防止委托的用户重置委托及其调用列表,并且只允许在调用列表中添加或删除目标
-
一些常用的事件尽量使用内置 EventHandler 或 EventHandler<TEventArgs>
-
发布者类引发一个事件(发布者确定何时引发事件),订阅者类注册一个事件并提供事件的处理方法(订阅者确定采取什么动作来响应)
-
处理事件的方法必须与委托声明的保持一致
-
使用 += 运算符注册事件;使用 -= 运算符取消订阅
-
尽量使用 EventHandler<TEventArgs> 来传递事件数据,TEventArgs 是泛型保证类型安全
-
可以继承 EventArgs 来自定义事件数据
-
事件可以声明为静态、虚拟、密封和抽象
-
接口可以包含事件
-
一个事件可以有多个订阅者
-
如果有多个订阅者,则同步调用事件处理程序。
最后,祝大家学习愉快!