イベントの発生

クラスでイベントを発生させるには、次の 3 つの要素を指定する必要があります。

  • イベント データを提供するクラス。

  • イベント デリゲート。

  • イベントを発生させるクラス。

イベント データを提供するクラスの定義

.NET Framework では、慣例に基づき、イベントが発生すると、イベント ハンドラーにイベント データが渡されます。 このイベント データは、System.EventArgs クラスまたはその派生クラスによって提供されます。

多くの場合、イベントにはカスタム データは含まれていません。イベントが発生したという事実に基づいて、イベント ハンドラーに必要な情報がすべて提供されます。 この場合、イベントは EventArgs オブジェクトをそのイベント ハンドラーに渡すことができます。 EventArgs クラスのメンバーは、System.Object から継承されない Empty だけです。 これは、新しい EventArgs クラスをインスタンス化するために使用できます。

イベントにカスタム データが含まれている場合、イベントは、EventArgs から派生したクラスのインスタンスをイベント ハンドラーに渡すことができます。 イベントがハンドラーに渡す正確なデータによっては、.NET Framework の既存のイベント データ クラスを使用できる場合があります。 たとえば、イベント ハンドラーでイベントに関連付けられているアクションを取り消すことができる場合は、CancelEventArgs クラスを使用できます。

イベント ハンドラーにカスタム データを提供する必要がある場合に、既存のクラスを使用できない場合は、独自のイベント データ クラスを定義できます。 このイベント データ クラスは、System.EventArgs から派生する必要があります。 慣例に基づき、このクラスには EventNameEventArgs 形式の名前が付けられます。 このようなカスタム イベント データ クラスの例を次に示します。 この例では、アラームを鳴らす時刻を示す読み取り専用の Time プロパティと、指定された間隔の後にアラームをもう一度鳴らすかどうか、または将来のアラームを取り消すかどうかを示す Snooze プロパティの 2 つのデータ項目をイベント ハンドラーに提供する AlarmEventArgs という名前のクラスが定義されます。

Public Class AlarmEventArgs : Inherits EventArgs
   Private alarmTime As Date
   Private snoozeOn As Boolean = True

   Public Sub New(time As Date)
      Me.alarmTime = time
   End Sub

   Public ReadOnly Property Time As Date
      Get
         Return Me.alarmTime
      End Get
   End Property

   Public Property Snooze As Boolean
      Get
         Return Me.snoozeOn
      End Get
      Set
         Me.snoozeOn = value
      End Set   
   End Property   
End Class
public class AlarmEventArgs : EventArgs
{
   private DateTime alarmTime;
   private bool snoozeOn = true;

   public AlarmEventArgs(DateTime time)
   {
      this.alarmTime = time;
   }

   public DateTime Time
   {
      get { return this.alarmTime; }
   }

   public bool Snooze
   {
      get { return this.snoozeOn; }
      set { this.snoozeOn = value; }
   }   
}
public ref class AlarmEventArgs : public EventArgs
{
private: 
   System::DateTime^ alarmTime;
   bool snoozeOn;

public:
   AlarmEventArgs(System::DateTime^ time) 
   {
      this->alarmTime = time;
      this->snoozeOn = true;
   }

   property DateTime^ Time 
   {
      System::DateTime^ get()
      { return this->alarmTime; }
   }

   property bool Snooze
   {
      bool get()
      { return this->snoozeOn; }
      void set(bool snooze)
      { this->snoozeOn = snooze; }
   }
};

イベントのデリゲートの定義

イベント デリゲートは、イベントのシグネチャを定義するために使用されます。 通常、特定のイベント デリゲートは、特定のイベント データ クラスに対応します。 慣例に基づき、.NET Framework のイベントには、EventName(sender, e) というシグネチャがあります。ここで、sender は、イベントを発生させたクラスまたは構造体への参照を提供する Object であり、e は、イベント データを提供する EventArgs オブジェクトまたは EventArgs の派生オブジェクトです。 したがって、デリゲート定義は、通常、EventNameHandler(sender, e) という形式になります。

.NET Framework クラス ライブラリまたはサードパーティ製ライブラリで既に定義されているイベント データ クラスを使用している場合は、対応するイベント デリゲートもそのライブラリで定義されている可能性があります。 たとえば、EventHandler デリゲートは、EventArgs クラスと共に使用できます。 同様に、CancelEventHandler デリゲートは、CancelEventArgs クラスと共に使用できます。

カスタム イベント データ クラスを定義する場合は、カスタム デリゲートを定義してイベント シグネチャを定義したり、ジェネリック Action<T1, T2> デリゲートを使用したりすることもできます。

AlarmEventHandler という名前のイベント デリゲートを定義する例を次に示します。

Public Delegate Sub AlarmEventHandler(sender As Object, e As AlarmEventArgs)
public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);
public delegate void AlarmEventHandler(System::Object^ sender, AlarmEventArgs^ e);

イベントを発生させるクラスの定義

イベントを発生させるクラスによって、イベント宣言が提供され、イベントを発生させるメソッドが定義される必要があります。 さらに、イベントを発生させるためのロジックもクラスのプロパティまたはメソッドに提供される必要があります。

event キーワード (C# の場合) または Event ステートメント (Visual Basic の場合) を使用して、クラスのイベント メンバーを定義します。 コンパイラがクラスでイベント宣言を検出すると、コンパイラは次に示すようなプライベート メンバーを作成します。

private EventNameHandler eh = null;

コンパイラは、add_EventName および remove_EventName という 2 つのパブリック メソッドも作成します。 これらのメソッドは、イベント デリゲート eh に対してデリゲートを結合または削除できるイベント フックです。 詳細はプログラマには見えないようになっています。

注意

C# および Visual Basic 2005 以外の言語では、イベント メンバーに対応するコードをコンパイラが自動的に生成しない場合があるため、イベント フックとプライベート デリゲート フィールドを明示的に定義する必要が生じる可能性があります。

AlarmEvent という名前のイベントを宣言する例を次に示します。 これは Alarm というクラスの例から抜粋です。このクラスの完全なソース コードは後で示します。 AlarmEventHandler デリゲートのシグネチャが含まれていることに注意してください。

Event AlarmEvent As AlarmEventHandler
public event AlarmEventHandler AlarmEvent;
public:
   event AlarmEventHandler^ AlarmEvent; 

イベント実装の定義が終了したら、イベントを発生させるタイミングを決定する必要があります。 イベントを発生させるには、イベントを定義したクラスまたは派生クラスにある OnEventName プロテクト メソッドを呼び出します。 次に、OnEventName メソッドがイベントを発生させます。

注意

プロテクトされた OnEventName メソッドを使用すると、デリゲートをアタッチせずに、派生クラスでイベントをオーバーライドすることもできます。登録されたデリゲートがイベントを必ず受信できるように、派生クラスは常に基本クラスの OnEventName メソッドを呼び出す必要があります。

AlarmEvent イベントを発生させる OnAlarmEvent メソッドを定義する例を次に示します。

Protected Sub OnAlarmEvent(e As AlarmEventArgs)
   RaiseEvent AlarmEvent(Me, e)
End Sub  
protected void OnAlarmEvent(AlarmEventArgs e)
{
   AlarmEvent(this, e);
}  
protected:
   void OnAlarmEvent(AlarmEventArgs^ e)
   {
      AlarmEvent(this, e);
   }

OnAlarmEvent メソッドを呼び出してイベントを発生させるロジックを含む、Set という名前のメソッドを定義する例を次に示します。 アラーム時刻の時間および分と現在の時刻の時間および分が等しい場合、Set メソッドは、AlarmEventArgs オブジェクトのインスタンスを生成し、このインスタンスにアラームを鳴らす時刻を設定します。 これは、イベント ハンドラーの実行後、Snooze プロパティの値を確認します。 Snooze が false の場合、アラーム イベントはそれ以上発生しないため、Set メソッドは終了します。 Snooze が true の場合は、アラームを鳴らす時刻が Interval プロパティの値でインクリメントされます。

Public Sub [Set]()
   Do
      System.Threading.Thread.Sleep(2000)
      Dim currentTime As DateTime = Date.Now
      ' Test whether it is time for the alarm to go off.
      If currentTime.Hour = alarmTime.Hour And _
         currentTime.Minute = AlarmTime.Minute Then
         Dim args As New AlarmEventArgs(currentTime)
         OnAlarmEvent(args)
         If args.Snooze = False Then 
            Exit Sub
         Else
            Me.alarmTime = Me.alarmTime.AddMinutes(Me.interval)
         End If      
      End If          
   Loop
End Sub 
public void Set()
{
   while (true) {
      System.Threading.Thread.Sleep(2000);
      DateTime currentTime = DateTime.Now;
      // Test whether it is time for the alarm to go off.
      if (currentTime.Hour == alarmTime.Hour && 
          currentTime.Minute == alarmTime.Minute)
      {    
         AlarmEventArgs args = new AlarmEventArgs(currentTime);
         OnAlarmEvent(args);
         if (! args.Snooze) 
            return;
         else
            this.alarmTime = this.alarmTime.AddMinutes(this.interval);
      }
   }
} 
void Set()
{
   do {
      Thread::Sleep(2000);
      System::DateTime^ currentTime = DateTime::Now;
      // Test whether it's time for the alarm to go off.
      if (currentTime->Hour == alarmTime->Hour && currentTime->Minute == alarmTime->Minute)
      {
         AlarmEventArgs^ args = gcnew AlarmEventArgs(currentTime);
         OnAlarmEvent(args);
         if (args->Snooze == false)
            return;
         else
            this->alarmTime = this->alarmTime->AddMinutes(this->interval);
      }
   } while (true);
}

Alarm クラスの完全なソース コードの例を次に示します。

Public Class Alarm
   Private alarmTime As Date
   Private interval As Integer = 10

   Event AlarmEvent As AlarmEventHandler

   Public Sub New(time As Date)
      Me.New(time, 10)
   End Sub

   Public Sub New(time As Date, interval As Integer)
      Me.alarmTime = time
      Me.interval = interval
   End Sub

   Public Sub [Set]()
      Do
         System.Threading.Thread.Sleep(2000)
         Dim currentTime As DateTime = Date.Now
         ' Test whether it is time for the alarm to go off.
         If currentTime.Hour = alarmTime.Hour And _
            currentTime.Minute = AlarmTime.Minute Then
            Dim args As New AlarmEventArgs(currentTime)
            OnAlarmEvent(args)
            If args.Snooze = False Then 
               Exit Sub
            Else
               Me.alarmTime = Me.alarmTime.AddMinutes(Me.interval)
            End If      
         End If          
      Loop
   End Sub 

   Protected Sub OnAlarmEvent(e As AlarmEventArgs)
      RaiseEvent AlarmEvent(Me, e)
   End Sub  
End Class
public class Alarm
{
   private DateTime alarmTime;
   private int interval = 10;

   public event AlarmEventHandler AlarmEvent;

   public Alarm(DateTime time) : this(time, 10)
   {
   }

   public Alarm(DateTime time, int interval)
   {
      this.alarmTime = time;
      this.interval = interval;
   }

   public void Set()
   {
      while (true) {
         System.Threading.Thread.Sleep(2000);
         DateTime currentTime = DateTime.Now;
         // Test whether it is time for the alarm to go off.
         if (currentTime.Hour == alarmTime.Hour && 
             currentTime.Minute == alarmTime.Minute)
         {    
            AlarmEventArgs args = new AlarmEventArgs(currentTime);
            OnAlarmEvent(args);
            if (! args.Snooze) 
               return;
            else
               this.alarmTime = this.alarmTime.AddMinutes(this.interval);
         }
      }
   } 

   protected void OnAlarmEvent(AlarmEventArgs e)
   {
      AlarmEvent(this, e);
   }  
}
public ref class Alarm 
{
private:
   System::DateTime^ alarmTime;
   int interval;

public:
   event AlarmEventHandler^ AlarmEvent; 
   Alarm(System::DateTime^ time) : alarmTime(time), interval(10) { };
   Alarm(System::DateTime^ time, int interval) : alarmTime(time), interval(interval) {};

   void Set()
   {
      do {
         Thread::Sleep(2000);
         System::DateTime^ currentTime = DateTime::Now;
         // Test whether it's time for the alarm to go off.
         if (currentTime->Hour == alarmTime->Hour && currentTime->Minute == alarmTime->Minute)
         {
            AlarmEventArgs^ args = gcnew AlarmEventArgs(currentTime);
            OnAlarmEvent(args);
            if (args->Snooze == false)
               return;
            else
               this->alarmTime = this->alarmTime->AddMinutes(this->interval);
         }
      } while (true);
   }

protected:
   void OnAlarmEvent(AlarmEventArgs^ e)
   {
      AlarmEvent(this, e);
   }
};

参照

処理手順

方法 : イベントを発生させる/処理する

方法 : クラスにイベントを実装する

概念

イベントとデリゲート

その他の技術情報

イベントの処理と発生