我需要使用哪种情况来使用C#的事件

时间:2023-01-17 07:08:18

I am studying events in C# but there are not much articles or information that show me where or what kinda position I'd need to use events in.

我正在研究C#中的事件,但没有太多的文章或信息告诉我在哪里或什么样的位置我需要使用事件。

Could some one give me real world example that makes them more understandable.

有人可以给我一些真实世界的例子,让他们更容易理解。

Thanks in advance.

提前致谢。

4 个解决方案

#1


As Chris Gray said, one use is to signal when something has happened that your code didn't directly call. The most common cause here is probably user actions on the GUI. Another example might be an asynchronous operation completing on another thread.

正如克里斯格雷所说,一个用途就是发出你的代码没有直接调用的事情。这里最常见的原因可能是GUI上的用户操作。另一个例子可能是在另一个线程上完成的异步操作。

The other reason to use events is when you don't know who might be interested in what has just happened. The class raising the event doesn't need to know (at design time) anything about how many instances of what other classes might be interested.

使用事件的另一个原因是,当您不知道谁可能对刚刚发生的事情感兴趣时。提升事件的类不需要知道(在设计时)任何关于其他类可能感兴趣的实例的数量。

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);
   }
}

Obviously, you also need to define the DidSomethingEventArgs class which passes on the relevant data about the event. This also illustrates a common naming convention for events. If the event is called X, then the event is only ever raised in a method called OnX and any data it passes on is an instance of class XEventArgs. Note that an event can be null if no listeners are subscribed to it, hence the check just before we raise the event.

显然,您还需要定义DidSomethingEventArgs类,该类传递有关事件的相关数据。这也说明了事件的常见命名约定。如果事件被称为X,那么事件只会在名为OnX的方法中引发,并且它传递的任何数据都是类XEventArgs的实例。请注意,如果没有订阅任何侦听器,则事件可以为null,因此在我们引发事件之前进行检查。

Note that this class knows nothing about what other classes might be interested in the fact that it did something. It simply announces the fact that it has done it.

请注意,这个类对其他类可能对它做了什么感兴趣的事情一无所知。它只是宣布它已经做到了这一事实。

Multiple classes can then listen out for the event:

然后,多个类可以监听事件:

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.
   }
}

And:

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.
   }
}

Now, when the DoSomething method is called on the Raiser instance, all instances of ListenerA and ListenerB will be informed via the DidSomething event. Note that the listener classes could easily be in different assemblies to the raiser. They need a reference back to the raiser's assembly but it doesn't need a reference to its listeners' assemblies.

现在,当在Raiser实例上调用DoSomething方法时,将通过DidSomething事件通知ListenerA和ListenerB的所有实例。请注意,侦听器类可以很容易地与提升者在不同的程序集中。他们需要一个引用回到raiser的程序集,但它不需要引用它的侦听器程序集。

Note that the above simple Raiser example may cause you some problems in a multi-threaded program. A more robust example would use something like:

请注意,上述简单的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
}

This ensures that another thread adding or removing a listener while you are in the middle of raising the event doesn't cause problems.

这可以确保在您处于引发事件的过程中添加或删除侦听器的另一个线程不会导致问题。

The simple listeners used here will also cause memory leaks if instances of the listener classes are being created and destroyed. This is because the Raiser instance gets passed (and stores) a reference to each listener as they subscribe to the event. This is enough to prevent the garbage collector from properly tidying up the listeners when all explicit references to them are removed. The best way round this is probably to make the listeners implement the IDisposable interface and to unsubscribe from the events in the Dispose method. Then you just need to remember to call the Dispose method.

如果正在创建和销毁侦听器类的实例,则此处使用的简单侦听器也将导致内存泄漏。这是因为Raiser实例在订阅事件时会传递(并存储)对每个侦听器的引用。这足以防止垃圾收集器在删除所有对它们的显式引用时正确整理侦听器。解决此问题的最佳方法可能是使侦听器实现IDisposable接口并取消订阅Dispose方法中的事件。然后你只需要记住调用Dispose方法。

#2


The most practical example I generally see is User Interactivity. Let's use a Button as a specific example. When the button is clicked, you obviously want something to happen. Let's say we call "SaveSettings()". However, we don't want to hard-code "SaveSettings()" into the button. The buttom would be commanding SaveSettings() to occur. Obviously, this prevents the button from being reusable - we can't use a button which calls SaveSettings() anywhere but the settings dialog. To avoid writing the same button code for every button, each one calling a different function, we use an event.

我通常看到的最实用的例子是用户交互。让我们使用Button作为一个具体的例子。单击按钮时,您显然希望发生某些事情。假设我们称之为“SaveSettings()”。但是,我们不希望将“SaveSettings()”硬编码到按钮中。 Buttom会命令SaveSettings()发生。显然,这会阻止按钮重复使用 - 除了设置对话框之外,我们不能使用任何调用SaveSettings()的按钮。为避免为每个按钮编写相同的按钮代码,每个按钮调用不同的功能,我们使用一个事件。

Instead of the button calling a function directly, the button announces that it has been clicked. From there, the button's responsibility is over. Other code can listen for that announcement, or event, and do something specific.

该按钮不是直接调用函数按钮,而是宣布它已被单击。从那里,按钮的责任就结束了。其他代码可以监听该公告或事件,并执行特定的操作。

So in our SaveSettings example, the settings dialog code finds the "OK" button and listens for its "I got clicked" announcement, and when it is fired, calls SaveSettings().

因此,在我们的SaveSettings示例中,设置对话框代码找到“确定”按钮并侦听其“我被点击”通知,当它被触发时,调用SaveSettings()。

Events can become very powerful because any number of different listeners can wait for the same event. Many things can be invoked by the event.

事件可以变得非常强大,因为任何数量的不同侦听器都可以等待同一事件。事件可以调用很多东西。

#3


Sure thing. think of an event as the notification that occurs when something completes in the system that your code didn’t directly call. In C# it's really easy to get code to run when an event "fires"

当然可以。将事件视为系统中某些内容完成时您的代码未直接调用的通知。在C#中,当事件“触发”时,运行代码非常容易

For example when a user presses a button an event will be raised or when a background network operation completes. In C# you use the += semantics to attach to the event that will be “signaled” when the event fires.

例如,当用户按下按钮时,将引发事件或者后台网络操作完成时。在C#中,您使用+ =语义附加到事件触发时将“发出信号”的事件。

I made you a simple C# winforms program – in it I added a button using the Visual Studio “Designer” (I just dragged a button from the Toolbox to the Window).

我为你做了一个简单的C#winforms程序 - 在其中我使用Visual Studio“Designer”添加了一个按钮(我只是将一个按钮从工具箱拖到窗口)。

You’ll see the line “button1.Click” – in this case I want to do something when the “Click” event is raised.

您将看到“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!");
        }
    }
}

You’ll also see other kinds of events in practice for example:

您还会在实践中看到其他类型的事件,例如:

  • Network operation has completed (WebClient.DownloadFileCompleted)
  • 网络操作已完成(WebClient.DownloadFileCompleted)

  • User Interfaces (resizing windows for example)
  • 用户界面(例如调整窗口大小)

  • Timers (set off the timer in 10 minutes)
  • 计时器(在10分钟内启动计时器)

#4


Let's say you are developing a UI. You create a widget and you add it to the main form. When something happens in your widget, you can use events to trigger some action on the form - disabling other buttons, etc.

假设您正在开发UI。您创建一个小部件,然后将其添加到主窗体。当您的小部件中发生某些事情时,您可以使用事件触发表单上的某些操作 - 禁用其他按钮等。

Just like how a button's click event works.

就像按钮的点击事件的工作方式一样。

#1


As Chris Gray said, one use is to signal when something has happened that your code didn't directly call. The most common cause here is probably user actions on the GUI. Another example might be an asynchronous operation completing on another thread.

正如克里斯格雷所说,一个用途就是发出你的代码没有直接调用的事情。这里最常见的原因可能是GUI上的用户操作。另一个例子可能是在另一个线程上完成的异步操作。

The other reason to use events is when you don't know who might be interested in what has just happened. The class raising the event doesn't need to know (at design time) anything about how many instances of what other classes might be interested.

使用事件的另一个原因是,当您不知道谁可能对刚刚发生的事情感兴趣时。提升事件的类不需要知道(在设计时)任何关于其他类可能感兴趣的实例的数量。

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);
   }
}

Obviously, you also need to define the DidSomethingEventArgs class which passes on the relevant data about the event. This also illustrates a common naming convention for events. If the event is called X, then the event is only ever raised in a method called OnX and any data it passes on is an instance of class XEventArgs. Note that an event can be null if no listeners are subscribed to it, hence the check just before we raise the event.

显然,您还需要定义DidSomethingEventArgs类,该类传递有关事件的相关数据。这也说明了事件的常见命名约定。如果事件被称为X,那么事件只会在名为OnX的方法中引发,并且它传递的任何数据都是类XEventArgs的实例。请注意,如果没有订阅任何侦听器,则事件可以为null,因此在我们引发事件之前进行检查。

Note that this class knows nothing about what other classes might be interested in the fact that it did something. It simply announces the fact that it has done it.

请注意,这个类对其他类可能对它做了什么感兴趣的事情一无所知。它只是宣布它已经做到了这一事实。

Multiple classes can then listen out for the event:

然后,多个类可以监听事件:

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.
   }
}

And:

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.
   }
}

Now, when the DoSomething method is called on the Raiser instance, all instances of ListenerA and ListenerB will be informed via the DidSomething event. Note that the listener classes could easily be in different assemblies to the raiser. They need a reference back to the raiser's assembly but it doesn't need a reference to its listeners' assemblies.

现在,当在Raiser实例上调用DoSomething方法时,将通过DidSomething事件通知ListenerA和ListenerB的所有实例。请注意,侦听器类可以很容易地与提升者在不同的程序集中。他们需要一个引用回到raiser的程序集,但它不需要引用它的侦听器程序集。

Note that the above simple Raiser example may cause you some problems in a multi-threaded program. A more robust example would use something like:

请注意,上述简单的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
}

This ensures that another thread adding or removing a listener while you are in the middle of raising the event doesn't cause problems.

这可以确保在您处于引发事件的过程中添加或删除侦听器的另一个线程不会导致问题。

The simple listeners used here will also cause memory leaks if instances of the listener classes are being created and destroyed. This is because the Raiser instance gets passed (and stores) a reference to each listener as they subscribe to the event. This is enough to prevent the garbage collector from properly tidying up the listeners when all explicit references to them are removed. The best way round this is probably to make the listeners implement the IDisposable interface and to unsubscribe from the events in the Dispose method. Then you just need to remember to call the Dispose method.

如果正在创建和销毁侦听器类的实例,则此处使用的简单侦听器也将导致内存泄漏。这是因为Raiser实例在订阅事件时会传递(并存储)对每个侦听器的引用。这足以防止垃圾收集器在删除所有对它们的显式引用时正确整理侦听器。解决此问题的最佳方法可能是使侦听器实现IDisposable接口并取消订阅Dispose方法中的事件。然后你只需要记住调用Dispose方法。

#2


The most practical example I generally see is User Interactivity. Let's use a Button as a specific example. When the button is clicked, you obviously want something to happen. Let's say we call "SaveSettings()". However, we don't want to hard-code "SaveSettings()" into the button. The buttom would be commanding SaveSettings() to occur. Obviously, this prevents the button from being reusable - we can't use a button which calls SaveSettings() anywhere but the settings dialog. To avoid writing the same button code for every button, each one calling a different function, we use an event.

我通常看到的最实用的例子是用户交互。让我们使用Button作为一个具体的例子。单击按钮时,您显然希望发生某些事情。假设我们称之为“SaveSettings()”。但是,我们不希望将“SaveSettings()”硬编码到按钮中。 Buttom会命令SaveSettings()发生。显然,这会阻止按钮重复使用 - 除了设置对话框之外,我们不能使用任何调用SaveSettings()的按钮。为避免为每个按钮编写相同的按钮代码,每个按钮调用不同的功能,我们使用一个事件。

Instead of the button calling a function directly, the button announces that it has been clicked. From there, the button's responsibility is over. Other code can listen for that announcement, or event, and do something specific.

该按钮不是直接调用函数按钮,而是宣布它已被单击。从那里,按钮的责任就结束了。其他代码可以监听该公告或事件,并执行特定的操作。

So in our SaveSettings example, the settings dialog code finds the "OK" button and listens for its "I got clicked" announcement, and when it is fired, calls SaveSettings().

因此,在我们的SaveSettings示例中,设置对话框代码找到“确定”按钮并侦听其“我被点击”通知,当它被触发时,调用SaveSettings()。

Events can become very powerful because any number of different listeners can wait for the same event. Many things can be invoked by the event.

事件可以变得非常强大,因为任何数量的不同侦听器都可以等待同一事件。事件可以调用很多东西。

#3


Sure thing. think of an event as the notification that occurs when something completes in the system that your code didn’t directly call. In C# it's really easy to get code to run when an event "fires"

当然可以。将事件视为系统中某些内容完成时您的代码未直接调用的通知。在C#中,当事件“触发”时,运行代码非常容易

For example when a user presses a button an event will be raised or when a background network operation completes. In C# you use the += semantics to attach to the event that will be “signaled” when the event fires.

例如,当用户按下按钮时,将引发事件或者后台网络操作完成时。在C#中,您使用+ =语义附加到事件触发时将“发出信号”的事件。

I made you a simple C# winforms program – in it I added a button using the Visual Studio “Designer” (I just dragged a button from the Toolbox to the Window).

我为你做了一个简单的C#winforms程序 - 在其中我使用Visual Studio“Designer”添加了一个按钮(我只是将一个按钮从工具箱拖到窗口)。

You’ll see the line “button1.Click” – in this case I want to do something when the “Click” event is raised.

您将看到“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!");
        }
    }
}

You’ll also see other kinds of events in practice for example:

您还会在实践中看到其他类型的事件,例如:

  • Network operation has completed (WebClient.DownloadFileCompleted)
  • 网络操作已完成(WebClient.DownloadFileCompleted)

  • User Interfaces (resizing windows for example)
  • 用户界面(例如调整窗口大小)

  • Timers (set off the timer in 10 minutes)
  • 计时器(在10分钟内启动计时器)

#4


Let's say you are developing a UI. You create a widget and you add it to the main form. When something happens in your widget, you can use events to trigger some action on the form - disabling other buttons, etc.

假设您正在开发UI。您创建一个小部件,然后将其添加到主窗体。当您的小部件中发生某些事情时,您可以使用事件触发表单上的某些操作 - 禁用其他按钮等。

Just like how a button's click event works.

就像按钮的点击事件的工作方式一样。