Какие ситуации мне нужно использовать события C # - PullRequest
0 голосов
/ 28 мая 2009

Я изучаю события в C #, но не так много статей или информации, показывающих мне, где или в какой позиции мне нужно использовать события.

Может ли кто-нибудь дать мне пример из реального мира, который сделает их более понятными.

Заранее спасибо.

Ответы [ 4 ]

5 голосов
/ 28 мая 2009

Как сказал Крис Грей, одно из применений - сигнализировать, когда что-то произошло, что ваш код не вызывал напрямую. Наиболее распространенной причиной здесь, вероятно, являются действия пользователя в графическом интерфейсе. Другим примером может быть асинхронная операция, завершающаяся в другом потоке.

Другая причина использовать события - это когда вы не знаете, кого может заинтересовать то, что только что произошло. Класс, вызывающий событие, не должен знать (во время разработки) что-либо о том, сколько экземпляров других классов может заинтересовать.

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. Обратите внимание, что событие может быть нулевым, если на него не подписаны слушатели, поэтому проверка перед тем, как мы вызовем событие.

Обратите внимание, что этот класс ничего не знает о том, какие другие классы могут быть заинтересованы в том, что он что-то сделал. Он просто объявляет о том, что сделал это.

Несколько классов могут прослушивать событие:

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

Теперь, когда метод DoSomething вызывается для экземпляра Raiser, все экземпляры ListenerA и ListenerB будут уведомляться через событие DidSomething. Обратите внимание, что классы слушателей могут легко находиться в разных сборках с рейзером Им нужна ссылка обратно на сборку сборщика, но не нужна ссылка на сборку его слушателей.

Обратите внимание, что приведенный выше простой пример 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.

2 голосов
/ 28 мая 2009

Самый практичный пример, который я обычно вижу, это интерактивность пользователя. Давайте использовать кнопку в качестве конкретного примера. Когда кнопка нажата, вы, очевидно, хотите, чтобы что-то произошло. Допустим, мы вызываем «SaveSettings ()». Однако мы не хотим жестко кодировать «SaveSettings ()» в кнопке. В качестве кнопки будет указана команда SaveSettings (). Очевидно, что это предотвращает возможность повторного использования кнопки - мы не можем использовать кнопку, которая вызывает SaveSettings () где-либо, кроме диалога настроек. Чтобы не писать одинаковый код кнопки для каждой кнопки, каждая из которых вызывает свою функцию, мы используем событие.

Вместо кнопки , вызывающей функцию напрямую, кнопка сообщает , что она была нажата. Оттуда ответственность кнопки заканчивается. Другой код может прослушивать это объявление или событие и делать что-то конкретное.

Таким образом, в нашем примере SaveSettings код диалогового окна настроек находит кнопку «ОК» и прослушивает ее объявление «Я получил нажатие», а при ее запуске вызывает SaveSettings ().

События могут стать очень мощными, потому что любое количество разных слушателей может ожидать одного и того же события. Событие может вызвать много вещей.

2 голосов
/ 28 мая 2009

Конечно. воспринимайте событие как уведомление, которое происходит, когда в системе что-то завершается, что ваш код не вызывал напрямую. В C # действительно легко получить код для запуска, когда событие «срабатывает»

Например, когда пользователь нажимает кнопку, возникает событие или когда завершается фоновая сетевая операция. В C # вы используете семантику + = для присоединения к событию, которое будет «сигнализировано» при возникновении события.

Я сделал вам простую программу winforms на C # - в нее я добавил кнопку с помощью 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!");
        }
    }
}

Вы также увидите на практике другие виды событий, например:

  • Сетевая операция завершена ( WebClient.DownloadFileCompleted )
  • Пользовательские интерфейсы (например, изменение размеров окон)
  • Таймеры (установить таймер на 10 минут)
1 голос
/ 28 мая 2009

Допустим, вы разрабатываете пользовательский интерфейс. Вы создаете виджет и добавляете его в основную форму. Когда что-то происходит в вашем виджете, вы можете использовать события для запуска какого-либо действия в форме - отключения других кнопок и т. Д.

Так же, как работает событие нажатия кнопки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...