我正在学习C#中的活动,但没有太多的文章或信息向我展示我需要在哪些地方或什么样的位置使用活动。
有人可以给我一些真实世界的例子,让他们更容易理解。
提前致谢。
答案 0 :(得分:5)
正如克里斯格雷所说,一个用途就是发出你的代码没有直接调用的事情。这里最常见的原因可能是GUI上的用户操作。另一个例子可能是在另一个线程上完成的异步操作。
使用事件的另一个原因是,当您不知道谁可能对刚刚发生的事情感兴趣时。提升事件的课程不需要(在设计时)了解其他课程可能感兴趣的实例数量。
class Raiser {
public DoSomething() {
//Do something long winded.
OnDidSomething(new DidSomethingEventArgs());
}
public EventHandler<DidSomethingEventArgs> DidSomething;
private OnDidSomething(DidSomethingEventArgs e) {
if (DidSomething != null)
DidSomething(this, e);
}
}
显然,您还需要定义DidSomethingEventArgs类,该类传递有关该事件的相关数据。这也说明了事件的常见命名约定。如果事件被称为X,则事件仅在名为OnX的方法中引发,并且它传递的任何数据都是类XEventArgs的实例。请注意,如果没有订阅任何侦听器,则事件可以为null,因此在我们引发事件之前进行检查。
请注意,这个类对其他类可能对它做了什么感兴趣的事情一无所知。它只是宣布它已经完成了它。
然后,多个班级可以监听该事件:
class ListenerA {
private Raiser r;
ListenerA(Raiser r) {
this.r = r;
r.DidSomething += R_DidSomething;
}
R_DidSomething(object sender, DidSomethingEventArgs e) {
//Do something with the result.
}
}
和
class ListenerB {
private Raiser r;
ListenerB(Raiser r) {
this.r = r;
r.DidSomething += R_DidSomething;
}
R_DidSomething(object sender, DidSomethingEventArgs e) {
//Do something with the result.
}
}
现在,当在Raiser实例上调用DoSomething方法时,将通过DidSomething事件通知ListenerA和ListenerB的所有实例。请注意,侦听器类可以很容易地与提升者在不同的程序集中。他们需要一个引用回到raiser的程序集,但它不需要引用它的监听器程序集。
请注意,上述简单的Raiser示例可能会在多线程程序中导致一些问题。一个更健壮的例子将使用类似的东西:
class Raiser {
public DoSomething() {
//Do something long winded.
OnDidSomething(new DidSomethingEventArgs());
}
#region DidSomething Event
private object _DidSomethingLock = new object();
private EventHandler<DidSomethingEventArgs> _DidSomething;
public EventHandler<DidSomethingEventArgs> DidSomething {
add { lock(_DidSomethinglock) _DidSomething += value; }
remove { lock(_DidSomethinglock) _DidSomething -= value; }
}
OnDidSomething(DidSomethingEventArgs e) {
EventHandler<DidSomethingEventArgs> handler;
lock (_DidSomethingLock)
handler = _DidSomething;
if (handler == null)
return;
try {
DidSomething(this, e);
} catch (Exception ex) {
//Do something with the exception
}
}
#endregion
}
这可以确保在您处于引发事件的过程中添加或删除侦听器的另一个线程不会导致问题。
如果正在创建和销毁侦听器类的实例,则此处使用的简单侦听器也将导致内存泄漏。这是因为Raiser实例在订阅事件时会传递(并存储)对每个侦听器的引用。这足以防止垃圾收集器在删除所有对它们的显式引用时正确整理侦听器。解决此问题的最佳方法可能是使侦听器实现IDisposable接口并取消订阅Dispose方法中的事件。然后你只需要记住调用Dispose方法。
答案 1 :(得分:2)
我通常看到的最实用的例子是用户交互。我们使用Button作为一个具体的例子。单击按钮时,您显然希望发生某些事情。假设我们称之为“SaveSettings()”。但是,我们不想将“SaveSettings()”硬编码到按钮中。 Buttom会命令SaveSettings()发生。显然,这会阻止按钮重复使用 - 除了设置对话框之外,我们不能使用任何调用SaveSettings()的按钮。为了避免为每个按钮编写相同的按钮代码,每个按钮调用不同的功能,我们使用一个事件。
按钮直接调用一个函数,而不是按钮宣布它已被点击。从那里,按钮的责任就结束了。其他代码可以监听该公告或事件,并执行特定的操作。
因此,在我们的SaveSettings示例中,设置对话框代码找到“确定”按钮并侦听其“我被点击”的公告,当它被触发时,调用SaveSettings()。
事件可以变得非常强大,因为任何数量的不同侦听器都可以等待同一事件。事件可以调用很多东西。
答案 2 :(得分:2)
当然可以。将事件视为系统中某些内容完成时您的代码未直接调用的通知。在C#中,当事件“触发”
时,可以很容易地运行代码例如,当用户按下按钮时,将引发事件或后台网络操作完成。在C#中,您使用+ =语义附加到事件触发时将“发出信号”的事件。
我为你制作了一个简单的C#winforms程序 - 在其中我使用Visual Studio“Designer”添加了一个按钮(我只是将一个按钮从工具箱拖到窗口)。
您将看到“button1.Click”行 - 在这种情况下,我想在引发“Click”事件时执行某些操作。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace events
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += new EventHandler(button1_Click);
}
void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hi!");
}
}
}
您还会在实践中看到其他类型的事件,例如:
答案 3 :(得分:1)
假设您正在开发UI。您创建一个小部件,然后将其添加到主窗体。当您的小部件中发生某些事情时,您可以使用事件触发表单上的某些操作 - 禁用其他按钮等。
就像按钮的点击事件一样。