私はイベントの目的を理解しています、特にユーザインタフェースを作成するという文脈の中で。これはイベントを作成するためのプロトタイプだと思います。
public void EventName(object sender, EventArgs e);
イベントハンドラは何をするのか、なぜ必要なのか、そしてどのように作成するのですか?
イベントハンドラを理解するには、理解する必要があります代表者。にC#つまり、デリゲートはメソッドへのポインタ(または参照)と考えることができます。ポインタは値として渡すことができるので、これは便利です。
デリゲートの中心的な概念は、その署名、つまり形状です。それは(1)戻り型と(2)入力引数です。たとえば、デリゲートを作成したとします。void MyDelegate(object sender, EventArgs e)
それは返すメソッドだけを指すことができますvoid
そして、object
そしてEventArgs
。四角い穴と四角い釘のようなもの。そのため、これらのメソッドはデリゲートと同じシグネチャ、つまり形状を持つと言います。
メソッドへの参照を作成する方法を理解した上で、イベントの目的について考えてみましょう。システム内の他の場所で何かが発生したときにコードを実行させる、または「イベントを処理する」。これを行うために、実行したいコードのための特定のメソッドを作成します。イベントと実行されるメソッドの間の接着剤はデリゲートです。イベントはイベントが発生したときに呼び出すメソッドへのポインタの "リスト"を内部的に格納しなければなりません。*もちろん、メソッドを呼び出せるようにするためには、どの引数を渡すかを知る必要があります。イベントと呼び出されるすべての特定のメソッドとの間の「コントラクト」としてデリゲートを使用します。
だからデフォルトEventHandler
(そしてそれに似た多くの)はメソッドの特定の形状(やはり、void / object-EventArgs)。あなたがイベントを宣言するとき、あなたは言っていますメソッドのどの形状(EventHandler)デリゲートを指定して、そのイベントが呼び出します。
//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);
//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;
//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
//Do some stuff
}
//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
//To raise the event within a method.
SomethingHappened("bar");
(*これが.NETのイベントへの鍵であり、「魔法」を取り除きます。イベントは、実際には、カバーの下にある、同じ「形状」のメソッドのリストにすぎません。リストは、イベントが存在する場所に格納されます。イベントは「発生」し、実際には「このメソッドのリストをたどり、これらの値をパラメータとして使用してそれぞれを呼び出す」だけです。イベントハンドラを割り当てることは、このメソッドのリストに自分のメソッドを追加する簡単で簡単な方法です。と呼ばれる)。
C#は2つの用語を知っています、delegate
そしてevent
。最初のものから始めましょう。
Adelegate
メソッドへの参照です。インスタンスへの参照を作成できるのと同じように、
MyClass instance = myFactory.GetInstance();
メソッドへの参照を作成するためにデリゲートを使うことができます:
Action myMethod = myFactory.GetInstance;
これでメソッドへの参照ができたので、参照を介してメソッドを呼び出すことができます。
MyClass instance = myMethod();
しかし、なぜあなたは?あなただけの電話もできますmyFactory.GetInstance()
直接。この場合はできます。ただし、アプリケーションの他の部分に知識を持たせたくない場合について考える必要があるケースは数多くあります。myFactory
または電話するmyFactory.GetInstance()
直接。
あなたが交換することができるようにしたい場合は明らかなものですmyFactory.GetInstance()
にmyOfflineFakeFactory.GetInstance()
1か所から(別名ファクトリーメソッドパターン)
あなたが持っているのであれば、TheOtherClass
クラスとそれは使用する必要がありますmyFactory.GetInstance()
これは、デリゲートなしでコードがどのように見えるかです(あなたに許可する必要があります)。TheOtherClass
あなたのタイプについて知っているmyFactory
):
TheOtherClass toc;
//...
toc.SetFactory(myFactory);
class TheOtherClass
{
public void SetFactory(MyFactory factory)
{
// set here
}
}
あなたがデリゲートを使うなら、あなたは私のファクトリのタイプを公開する必要はありません:
TheOtherClass toc;
//...
Action factoryMethod = myFactory.GetInstance;
toc.SetFactoryMethod(factoryMethod);
class TheOtherClass
{
public void SetFactoryMethod(Action factoryMethod)
{
// set here
}
}
このように、あなたは彼らにあなたのタイプを公開せずに、使用するために他のクラスにデリゲートを与えることができます。あなたが公開しているのはあなたのメソッドのシグネチャだけです(あなたが持っているパラメータの数など)。
「私の方法の署名」私は以前どこでそれを聞きましたか?はい、インターフェース!インタフェースはクラス全体のシグネチャを記述します。デリゲートは1つのメソッドのシグネチャを記述するものと考えてください。
インターフェースとデリゲートのもう一つの大きな違いは、クラスを書いているとき、C#に「このメソッドはそのタイプのデリゲートを実装する」と言う必要がないということです。インターフェースでは、「このクラスはそのタイプのインターフェースを実装する」と言う必要があります。
さらに、デリゲート参照は(いくつかの制限はありますが、下記を参照してください)複数のメソッドを参照できます。MulticastDelegate
)つまり、デリゲートを呼び出すと、明示的にアタッチされた複数のメソッドが実行されます。オブジェクト参照は常に1つのオブジェクトのみを参照できます。
の制限MulticastDelegate
(メソッド/デリゲート)シグネチャは戻り値を持つべきではないということです。void
)とキーワードout
そしてref
署名には使用されません。明らかに、数値を返す2つのメソッドを呼び出すことはできず、それらが同じ数値を返すことを期待することはできません。署名が準拠すると、代理人は自動的にMulticastDelegate
。
イベントは単なるプロパティ(get、set、インスタンスフィールドへのプロパティなど)で、他のオブジェクトからデリゲートへのサブスクリプションを公開します。これらのプロパティは、get; set;をサポートしていません。代わりに、それらはadd; removeをサポートします。
だからあなたは持つことができます:
Action myField;
public event Action MyProperty
{
add { myField += value; }
remove { myField -= value; }
}
つまり、デリゲートはメソッドへの参照であり、デリゲートから参照されるメソッドを自分たちに提供できるということを世界に知らせるためのイベントを作成できるということです。そして、UIボタンです。私がクリックされたかどうかに興味がある人は誰でも私たちに彼らのメソッドを登録するように頼むことができます(私たちが公開するイベントを通して)。私たちは私たちに与えられたすべてのそれらの方法を使用することができて、そして我々の代表によってそれらを参照することができます。そして、ユーザーがそのボタンをクリックしてクリックするまで待ちます。そして、デリゲートを起動する十分な理由があります。そして、デリゲートは私たちに与えられたそれらすべてのメソッドを参照するので、それらすべてのメソッドが呼び出されます。これらのメソッドが何をするのかもわからないし、どのクラスがそれらのメソッドを実装しているのかもわからない。私たちが気にしているのは、誰かがクリックされることに興味を持っていること、そして私たちの希望するシグネチャに準拠した方法への参照を私たちに与えたことだけです。
Javaのような言語にはデリゲートはありません。代わりにインターフェースを使用します。彼らがそうする方法は、「私たちがクリックされる」ことに興味を持っている人に、(私たちが呼ぶことができるある方法で)あるインターフェースを実装するように頼み、そしてインターフェースを実装するインスタンス全体を私たちに与えることです。このインターフェースを実装しているすべてのオブジェクトのリストを保持しており、クリックされたときはいつでもそれらの「呼び出すことができる特定のメソッド」を呼び出すことができます。
event
単なる構文糖であり、それ以上のものではありません。 - Mathieu Guindon
これは助けるかもしれないコード例です:
using System;
using System.Collections.Generic;
using System.Text;
namespace Event_Example
{
// First we have to define a delegate that acts as a signature for the
// function that is ultimately called when the event is triggered.
// You will notice that the second parameter is of MyEventArgs type.
// This object will contain information about the triggered event.
public delegate void MyEventHandler(object source, MyEventArgs e);
// This is a class which describes the event to the class that receives it.
// An EventArgs class must always derive from System.EventArgs.
public class MyEventArgs : EventArgs
{
private string EventInfo;
public MyEventArgs(string Text) {
EventInfo = Text;
}
public string GetInfo() {
return EventInfo;
}
}
// This next class is the one which contains an event and triggers it
// once an action is performed. For example, lets trigger this event
// once a variable is incremented over a particular value. Notice the
// event uses the MyEventHandler delegate to create a signature
// for the called function.
public class MyClass
{
public event MyEventHandler OnMaximum;
private int i;
private int Maximum = 10;
public int MyValue
{
get { return i; }
set
{
if(value <= Maximum) {
i = value;
}
else
{
// To make sure we only trigger the event if a handler is present
// we check the event to make sure it's not null.
if(OnMaximum != null) {
OnMaximum(this, new MyEventArgs("You've entered " +
value.ToString() +
", but the maximum is " +
Maximum.ToString()));
}
}
}
}
}
class Program
{
// This is the actual method that will be assigned to the event handler
// within the above class. This is where we perform an action once the
// event has been triggered.
static void MaximumReached(object source, MyEventArgs e) {
Console.WriteLine(e.GetInfo());
}
static void Main(string[] args) {
// Now lets test the event contained in the above class.
MyClass MyObject = new MyClass();
MyObject.OnMaximum += new MyEventHandler(MaximumReached);
for(int x = 0; x <= 15; x++) {
MyObject.MyValue = x;
}
Console.ReadLine();
}
}
}
OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
- Rango
これは実際にはイベントハンドラ、つまりイベントが発生したときに呼び出されるメソッドの宣言です。イベントを作成するには、次のように書きます。
public class Foo
{
public event EventHandler MyEvent;
}
そして、あなたはこのようなイベントを購読することができます:
Foo foo = new Foo();
foo.MyEvent += new EventHandler(this.OnMyEvent);
OnMyEvent()を次のように定義すると、
private void OnMyEvent(object sender, EventArgs e)
{
MessageBox.Show("MyEvent fired!");
}
いつでもFoo
発射するMyEvent
それからあなたのOnMyEvent
ハンドラが呼び出されます。
あなたはいつものインスタンスを使用する必要はありませんEventArgs
2番目のパラメータとして。追加情報を含めたい場合は、から派生したクラスを使用できます。EventArgs
(EventArgs
慣例により基本です。たとえば、次のように定義されているイベントをいくつか見てみましょう。Control
WinFormの場合FrameworkElement
WPFでは、追加情報をイベントハンドラに渡すイベントの例を見ることができます。
OnXXX
イベントハンドラの命名パターン(愚かなことに、OnXXXはMFCで'を処理し、.netでは'を上げると解釈されるため、その意味は不明瞭でわかりにくい - 詳細についてはこの記事を参照してください)好ましい名前はRaiseXXX
イベントを開催するHandleXXX
またはSender_XXX
イベントハンドラのために。 - Jason Williams
ここに既存の素晴らしい答えを追加するために - 受け入れられたもののコードの上に構築するdelegate void MyEventHandler(string foo)
...
コンパイラはのデリゲート型を知っているので何かあったイベント、これ:
myObj.SomethingHappened += HandleSomethingHappened;
完全に同等です:
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
そしてハンドラーもすることができます未登録と-=
このような:
// -= removes the handler from the event's list of "listeners":
myObj.SomethingHappened -= HandleSomethingHappened;
完全を期すために、イベントを発生させることは、イベントを所有するクラスでのみ、このようにすることができます。
//Firing the event is done by simply providing the arguments to the event:
var handler = SomethingHappened; // thread-local copy of the event
if (handler != null) // the event is null if there are no listeners!
{
handler("Hi there!");
}
ハンドラーのスレッドローカルコピーは、呼び出しがスレッドセーフであることを確認するために必要です。そうでなければ、スレッドがイベントかどうかを確認した直後にスレッドがイベントの最後のハンドラーを登録解除する可能性があります。null
そして、私たちは「楽しい」を持っているでしょうNullReferenceException
そこ。
C#6はこのパターンのための素晴らしい手書きを導入しました。 NULL伝播演算子を使用します。
SomethingHappened?.Invoke("Hi there!");
私の出来事に対する理解は、
代表者:
実行されるメソッドへの参照を保持するための変数。これにより、変数のようにメソッドをやり取りすることが可能になります。
イベントを作成して呼び出す手順
イベントはデリゲートのインスタンスです
イベントはデリゲートのインスタンスなので、まずデリゲートを定義する必要があります。
イベントが発生したときに実行される1つまたは複数のメソッドを割り当てます(代理人を呼び出す)
イベントを開始する(代理人に電話する)
例:
using System;
namespace test{
class MyTestApp{
//The Event Handler declaration
public delegate void EventHandler();
//The Event declaration
public event EventHandler MyHandler;
//The method to call
public void Hello(){
Console.WriteLine("Hello World of events!");
}
public static void Main(){
MyTestApp TestApp = new MyTestApp();
//Assign the method to be called when the event is fired
TestApp.MyHandler = new EventHandler(TestApp.Hello);
//Firing the event
if (TestApp.MyHandler != null){
TestApp.MyHandler();
}
}
}
}
発行者:イベントが発生する場所。パブリッシャは、クラスが使用しているデリゲートを指定して必要な引数を生成し、それらの引数とそれ自体をデリゲートに渡します。
加入者:応答が発生した場所。加入者はイベントに応答する方法を指定する必要があります。これらのメソッドは、デリゲートと同じ種類の引数を取ります。その後、購読者はこのメソッドを発行者の代理人に追加します。
したがって、イベントがパブリッシャーで発生すると、デリゲートはいくつかのイベント引数(データなど)を受け取りますが、パブリッシャーはこれらすべてのデータで何が起こるのかわかりません。購読者は、自分のクラスにメソッドを作成して発行者のクラスのイベントに応答することができます。これにより、購読者は発行者のイベントに応答することができます。
//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyDelegate(string foo);
//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyDelegate MyEvent;
//Here is some code I want to be executed
//when SomethingHappened fires.
void MyEventHandler(string foo)
{
//Do some stuff
}
//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.MyEvent += new MyDelegate (MyEventHandler);
私はKE50に同意しますが、イベントは実行されるべきアクションの集合(すなわちデリゲート)を保持しているので、 'event'キーワードを 'ActionCollection'のエイリアスと見なします。
using System;
namespace test{
class MyTestApp{
//The Event Handler declaration
public delegate void EventAction();
//The Event Action Collection
//Equivalent to
// public List<EventAction> EventActions=new List<EventAction>();
//
public event EventAction EventActions;
//An Action
public void Hello(){
Console.WriteLine("Hello World of events!");
}
//Another Action
public void Goodbye(){
Console.WriteLine("Goodbye Cruel World of events!");
}
public static void Main(){
MyTestApp TestApp = new MyTestApp();
//Add actions to the collection
TestApp.EventActions += TestApp.Hello;
TestApp.EventActions += TestApp.Goodbye;
//Invoke all event actions
if (TestApp.EventActions!= null){
//this peculiar syntax hides the invoke
TestApp.EventActions();
//using the 'ActionCollection' idea:
// foreach(EventAction action in TestApp.EventActions)
// action.Invoke();
}
}
}
}