C# 事件处理(与 Java 相比)
本文关键字:相比 Java 事件处理 | 更新日期: 2023-09-27 17:47:47
我目前很难理解和使用德拉加特在 C# 中实现事件。 我已经习惯了Java的做事方式:
- 为侦听器类型定义一个接口,该接口将包含许多方法定义
- 为该接口定义适配器类,以便在我对侦听器中定义的所有事件不感兴趣时使事情变得更容易
- 在引发事件的类中定义 Add、Remove 和 Get[] 方法
- 定义受保护的 fire 方法以执行遍历添加侦听器列表并调用正确方法的繁琐工作
我理解(并且喜欢!( - 我知道我可以在 c# 中完全一样地做到这一点,但似乎一个新的(更好的?(系统已经为 c# 到位。 在阅读了无数解释 c# 中委托和事件的用法的教程之后,我仍然没有真正理解发生了什么:S
简而言之,对于以下方法,我将如何在 c# 中实现事件系统:
void computerStarted(Computer computer);
void computerStopped(Computer computer);
void computerReset(Computer computer);
void computerError(Computer computer, Exception error);
^ 上述方法取自我曾经制作的Java应用程序,我正在尝试将其移植到c#。
非常感谢!
您将创建四个事件,以及引发它们的方法,以及一个新的基于 EventArgs 的类来指示错误:
public class ExceptionEventArgs : EventArgs
{
private readonly Exception error;
public ExceptionEventArgs(Exception error)
{
this.error = error;
}
public Error
{
get { return error; }
}
}
public class Computer
{
public event EventHandler Started = delegate{};
public event EventHandler Stopped = delegate{};
public event EventHandler Reset = delegate{};
public event EventHandler<ExceptionEventArgs> Error = delegate{};
protected void OnStarted()
{
Started(this, EventArgs.Empty);
}
protected void OnStopped()
{
Stopped(this, EventArgs.Empty);
}
protected void OnReset()
{
Reset(this, EventArgs.Empty);
}
protected void OnError(Exception e)
{
Error(this, new ExceptionEventArgs(e));
}
}
然后,类将使用方法或匿名函数订阅事件:
someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{
Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");
关于上述几点需要注意:
- OnXXX 方法受到保护,以便派生类可以引发事件。这并不总是必要的 - 按照您认为合适的方式进行。
- 每个事件声明上的
delegate{}
部分只是避免必须执行 null 检查的技巧。它为每个事件订阅一个无操作事件处理程序 - 事件声明是类似字段的事件。实际创建的既是一个变量,也是一个事件。在类中,您会看到变量;在课堂之外,您会看到事件。
有关事件的更多详细信息,请参阅我的事件/委托文章。
您必须为此定义一个委托
public delegate void ComputerEvent(object sender, ComputerEventArgs e);
ComputerEventArgs 将定义如下:
public class ComputerEventArgs : EventArgs
{
// TODO wrap in properties
public Computer computer;
public Exception error;
public ComputerEventArgs(Computer aComputer, Exception anError)
{
computer = aComputer;
error = anError;
}
public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
{
}
}
触发事件的类将具有以下功能:
public YourClass
{
...
public event ComputerEvent ComputerStarted;
public event ComputerEvent ComputerStopped;
public event ComputerEvent ComputerReset;
public event ComputerEvent ComputerError;
...
}
以下是为事件分配处理程序的方式:
YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);
您的处理程序是:
private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
// do your thing.
}
主要区别在于,在 C# 中,事件不是基于接口的。相反,事件发布者声明委托,您可以将委托视为函数指针(尽管不完全相同:-((。然后,订阅服务器将事件原型实现为常规方法,并将委托的新实例添加到发布者的事件处理程序链中。阅读有关代表和活动的更多信息。
您还可以在此处阅读 C# 与 Java 事件的简短比较。
首先,.Net 中有一个通常用于事件的标准方法签名。这些语言允许将任何类型的方法签名用于事件,并且有一些专家认为该约定存在缺陷(我大多同意(,但它就是这样,我将在本例中遵循它。
- 创建一个将包含事件参数(派生自 EventArgs(的类。
公共类 ComputerEventArgs : EventArgs{ 电脑电脑; 构造函数、属性等}
- 在要触发事件的类上创建一个公共事件。
类计算机事件生成器//我选了一个可怕的名字 顺便说一句。 { public event EventHandlerComputerStarted; 公共事件事件处理程序<计算机事件参数>计算机已停止; 公共事件事件处理程序<计算机事件参数>计算机重置; ... }计算机事件参数>计算机事件参数>
- 调用事件。
类计算机事件生成器 { ... 私人无效 计算机启动(计算机( { EventHandlertemp = ComputerStarted; if (temp != null( temp(this, new ComputerEventArgs(computer((;如果事件是静态的,则将"this"替换为 null } }
- 附加事件的处理程序。
void OnLoad(( { ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator((; computerEventGenerator.ComputerStarted += new EventHandler(ComputerEventGenerator_ComputerStarted(; }
- 创建刚刚附加的处理程序(主要是通过在 VS 中按 Tab 键(。
private void ComputerEventGenerator_ComputerStarted(对象发送器,ComputerEventArgs args( { if (args.Computer.Name == "HAL9000"( ShutItDownNow(args.计算机(; }
- 完成后不要忘记分离处理程序。(忘记这样做是 C# 中内存泄漏的最大来源!
void OnClose(( { ComputerEventGenerator.ComputerStarted -= ComputerEventGenerator_ComputerStarted; }
就是这样!
编辑:老实说,我不明白为什么我的编号点都显示为"1"。我讨厌电脑。
几种方法可以做你想做的事情。最直接的方法是为宿主类中的每个事件定义委托,例如
public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
if (ComputerStarted != null)
{
ComputerStarted.Invoke(computer);
}
}
protected void someMethod()
{
//...
computer.Started = true; //or whatever
OnComputerStarted(computer);
//...
}
任何对象都可以通过以下方式"侦听"此事件:
Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
this.ComputerStartedHandler);
protected void ComputerStartedHandler(Computer computer)
{
//do something
}
执行此操作的"推荐标准方法"是定义 EventArgs 的子类来保存计算机(以及旧/新状态和异常(值,将 4 个委托减少到 1 个。在这种情况下,这将是一个更干净的解决方案,特别是使用用于计算机状态的枚举,以防以后扩展。但基本技术保持不变:
- 委托定义事件处理程序/侦听器的签名/接口
- 事件数据成员是"侦听器"的列表
使用 -= 语法而不是 += 删除侦听器
在 c# 中,事件是委托。它们的行为方式与 C/C++ 中的函数指针类似,但它们是从 System.Delegate 派生的实际类。
在这种情况下,请创建自定义 EventArgs 类来传递计算机对象。
public class ComputerEventArgs : EventArgs
{
private Computer _computer;
public ComputerEventArgs(Computer computer) {
_computer = computer;
}
public Computer Computer { get { return _computer; } }
}
然后公开来自生产者的事件:
public class ComputerEventProducer
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerEventArgs> Error;
/*
// Invokes the Started event */
private void OnStarted(Computer computer) {
if( Started != null ) {
Started(this, new ComputerEventArgs(computer));
}
}
// Add OnStopped, OnReset and OnError
}
然后,事件的使用者将处理程序函数绑定到使用者上的每个事件。
public class ComputerEventConsumer
{
public void ComputerEventConsumer(ComputerEventProducer producer) {
producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
// Add other event handlers
}
private void ComputerStarted(object sender, ComputerEventArgs e) {
}
}
当 ComputerEventProducer 调用 OnStarted 时,将调用 Started 事件,该事件又将调用 ComputerEventConsumer.ComputerStarted 方法。
委托声明函数签名,当它用作类上的事件时,它还充当登记的调用目标的集合。事件的 += 和 -= 语法用于将目标添加到列表中。
给定以下委托用作事件:
// arguments for events
public class ComputerEventArgs : EventArgs
{
public Computer Computer { get; set; }
}
public class ComputerErrorEventArgs : ComputerEventArgs
{
public Exception Error { get; set; }
}
// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);
public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);
// component that raises events
public class Thing
{
public event ComputerEventHandler Started;
public event ComputerEventHandler Stopped;
public event ComputerEventHandler Reset;
public event ComputerErrorEventHandler Error;
}
您将通过以下方式订阅这些事件:
class Program
{
static void Main(string[] args)
{
var thing = new Thing();
thing.Started += thing_Started;
}
static void thing_Started(object sender, ComputerEventArgs e)
{
throw new NotImplementedException();
}
}
尽管参数可以是任何内容,但对象发送器和 EventArgs e 是一种非常一致使用的约定。+= thing_started将首先创建指向目标方法的委托实例,然后将其添加到事件中。
在组件本身上,您通常会添加方法来触发事件:
public class Thing
{
public event ComputerEventHandler Started;
public void OnStarted(Computer computer)
{
if (Started != null)
Started(this, new ComputerEventArgs {Computer = computer});
}
}
您必须测试 null,以防未向事件添加任何委托。但是,当您进行方法调用时,将调用所有已添加的委托。这就是为什么对于事件,返回类型为 void - 没有单个返回值 - 因此要反馈信息,您将在 EventArgs 上拥有事件处理程序将更改的属性。
另一个改进是使用通用事件处理程序委托,而不是为每种类型的参数声明一个具体的委托。
public class Thing
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerErrorEventArgs> Error;
}
非常感谢大家的回答! 终于,我开始明白发生了什么。 只有一件事; 似乎如果每个事件都有不同数量/类型的参数,我需要创建一个不同的 :: EventArgs 类来处理它:
public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);
这需要三个类来处理事件!?(两个自定义,一个使用默认的 EventArgs.Empty 类(
干杯!
好的,最后澄清!:所以这几乎是我在代码方面可以做的最好的事情来实现这些事件?
public class Computer {
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler Reset;
public event EventHandler<BreakPointEvent> BreakPointHit;
public event EventHandler<ExceptionEvent> Error;
public Computer() {
Started = delegate { };
Stopped = delegate { };
Reset = delegate { };
BreakPointHit = delegate { };
Error = delegate { };
}
protected void OnStarted() {
Started(this, EventArgs.Empty);
}
protected void OnStopped() {
Stopped(this, EventArgs.Empty);
}
protected void OnReset() {
Reset(this, EventArgs.Empty);
}
protected void OnBreakPointHit(int breakPoint) {
BreakPointHit(this, new BreakPointEvent(breakPoint));
}
protected void OnError(System.Exception exception) {
Error(this, new ExceptionEvent(exception));
}
}
}