イベントの目的、特にユーザーインターフェースを作成する状況について理解しています。これはイベントを作成するためのプロトタイプだと思います:
public void EventName(object sender, EventArgs e);
イベントハンドラーは何をしますか、なぜそれらが必要なのですか、それをどのように作成しますか?
イベントの目的、特にユーザーインターフェースを作成する状況について理解しています。これはイベントを作成するためのプロトタイプだと思います:
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#は、二つの用語を知っている、delegate
とevent
。最初のものから始めましょう。
A delegate
はメソッドへの参照です。インスタンスへの参照を作成できるのと同じように:
MyClass instance = myFactory.GetInstance();
デリゲートを使用して、メソッドへの参照を作成できます。
Action myMethod = myFactory.GetInstance;
これでメソッドへの参照ができたので、参照を介してメソッドを呼び出すことができます。
MyClass instance = myMethod();
しかし、なぜあなたは?myFactory.GetInstance()
直接電話することもできます。この場合はできます。ただし、アプリケーションの残りの部分に知識を持たせmyFactory
たり、myFactory.GetInstance()
直接呼び出したりしたくない場合については、多くの場合を考えます。
あなたは置き換えることができるようにしたい場合は明白なものであるmyFactory.GetInstance()
にmyOfflineFakeFactory.GetInstance()
一つの中央の場所(別名からFactory Methodパターン)。
したがって、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つのメソッドのシグネチャのみを説明していると考えてください。
インターフェースとデリゲートのもう1つの大きな違いは、クラスを作成するときに、C#に「このメソッドはそのタイプのデリゲートを実装する」と言う必要がないことです。インターフェースの場合、「このクラスはそのタイプのインターフェースを実装する」と言う必要があります。
さらに、デリゲート参照は(いくつかの制限付きで、以下を参照)複数のメソッド(と呼ばれるMulticastDelegate
)を参照できます。つまり、デリゲートを呼び出すと、明示的に接続された複数のメソッドが実行されます。オブジェクト参照は常に1つのオブジェクトのみを参照できます。
以下のための制限は、MulticastDelegate
(メソッド/デリゲート)署名が任意の戻り値を(持っているべきではないとしているvoid
)と、キーワードout
とref
署名に使用されていません。明らかに、数値を返す2つのメソッドを呼び出して、それらが同じ数値を返すことを期待することはできません。署名が準拠すると、デリゲートは自動的にになりMulticastDelegate
ます。
イベントは、他のオブジェクトからのデリゲートへのサブスクリプションを公開する単なるプロパティ(get; set;インスタンスフィールドのプロパティなど)です。ただし、これらのプロパティはget; set;をサポートしていません。代わりに、追加をサポートしています。削除する;
だからあなたは持つことができます:
Action myField;
public event Action MyProperty
{
add { myField += value; }
remove { myField -= value; }
}
これで、デリゲートがメソッドへの参照であり、デリゲートから参照されるメソッドを提供できることを世界中に知らせるイベントを作成できることがわかりました。それから、UIボタンです。私がクリックされたかどうかに興味がある人なら誰でも、その方法を(登録したイベントを介して)登録するように依頼できます。与えられたメソッドをすべて使用して、デリゲートから参照できます。次に、ユーザーが来てそのボタンをクリックするまで待機します。それから、デリゲートを呼び出す十分な理由があります。また、デリゲートは指定されたすべてのメソッドを参照するため、これらのすべてのメソッドが呼び出されます。これらのメソッドが何を実行するのか、またどのクラスがそれらのメソッドを実装するのかもわかりません。私たちが気にしているのは、誰かがクリックされることに興味を持っていたことです。
Javaのような言語にはデリゲートはありません。代わりにインターフェースを使用します。彼らが行う方法は、「私たちがクリックされる」ことに関心のある人に、特定のインターフェースを(私たちが呼び出すことができる特定のメソッドで)実装するように依頼し、そのインターフェースを実装するインスタンス全体を提供することです。このインターフェイスを実装するすべてのオブジェクトのリストを保持しており、クリックされるたびに「呼び出すことができる特定のメソッド」を呼び出すことができます。
event
です。これは単なる構文糖であり、それ以上のものではありません。
これは、実際にはイベントハンドラの宣言です。イベントが発生すると呼び出されるメソッドです。イベントを作成するには、次のように記述します。
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
、WinFormsまたはFrameworkElement
WPFで定義されているいくつかのイベントを見ると、イベントハンドラーに追加情報を渡すイベントの例を見ることができます。
OnXXX
イベントハンドラーの命名パターンを使用しないことをお勧めします。(愚かにも、OnXXXはMFCで「ハンドルXXX」を意味し、.netで「raise XXX」を意味するように解釈されているため、その意味は不明確で紛らわしい- 詳細はこの投稿を参照)。優先される名前はRaiseXXX
、イベントを発生させること、HandleXXX
またはSender_XXX
イベントハンドラーの名前です。
これは役立つかもしれないコード例です:
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..."));
ここで既存の素晴らしい答えに追加するだけです-受け入れられたもののコードに基づいて構築しdelegate void MyEventHandler(string foo)
ます...
コンパイラは、SomethingHappenedイベントのデリゲート型を知っているため、次のようになります。
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();
}
}
}
}
投稿で素晴らしい技術的回答!それに技術的に追加するものは何もありません。
言語やソフトウェア全般に新機能が登場する主な理由の1つは、マーケティングや会社の政治です。:-)これは概算を下回ってはなりません!
これは、デリゲートとイベントの特定の範囲にも当てはまると思います。私はそれらが有用であるとわかり、C#言語に価値を加えていますが、一方でJava言語はそれらを使用しないことにしました!彼らはあなたがデリゲートで解決しているものは何でも、言語の既存の機能、すなわちインターフェースですでに解決できると決めました
2001年頃、Microsoftは.NETフレームワークとC#言語をJavaの競合ソリューションとしてリリースしました。そのため、Javaにはない新しい機能があるのは良かったです。
最近、c#でイベントを使用する方法の例を作成し、ブログに投稿しました。私は非常に単純な例で、それをできるだけ明確にしようとしました。誰かを助けるかもしれない場合は、ここにあります:http : //www.konsfik.com/using-events-in-csharp/
説明とソースコード(多くのコメントを含む)が含まれ、主にイベントとイベントハンドラーの適切な(テンプレートのような)使用法に焦点を当てています。
重要なポイントは次のとおりです。
イベントは「サブタイプのデリゲート」のようなものであり、(良い意味で)より制約されています。実際、イベントの宣言には常にデリゲートが含まれます(EventHandlerはデリゲートの一種です)。
イベントハンドラーは、特定の種類のデリゲート(テンプレートと考えることもできます)であり、特定の "署名"を持つイベントをユーザーに作成させる必要があります。署名の形式は次のとおりです(オブジェクト送信者、EventArgsイベント引数)。
イベントが伝える必要のあるあらゆるタイプの情報を含めるために、EventArgsの独自のサブクラスを作成できます。イベントを使用する場合は、EventHandlerを使用する必要はありません。それらを完全にスキップして、代わりに独自のデリゲートを使用できます。
イベントとデリゲートの使用の主な違いの1つは、イベントはパブリックとして宣言されていても、宣言されたクラス内からしか呼び出せないことです。これは非常に重要な違いです。イベントを公開して外部メソッドに「接続」すると同時に、「外部の誤用」から保護するためです。
もう1つ知っておくべきこと、場合によっては、低レベルの結合が必要なときにデリゲート/イベントを使用する必要があります。
アプリケーションの複数の場所でコンポーネントを使用する場合は、低レベルのカップリングでコンポーネントを作成する必要があり、特定の関係のないロジックをコンポーネントの外部で委任する必要があります。これにより、分離されたシステムとクリーンなコードが確実に得られます。
SOLID原理これは" D "、(Dの ependency反転原理)。
「IoC」とも呼ばれ、制御の反転。
イベント、デリゲート、DI(依存性注入)で「IoC」を作成できます。
子クラスのメソッドにアクセスするのは簡単です。しかし、子から親クラスのメソッドにアクセスするのはより困難です。親参照を子に渡す必要があります!(またはインターフェイスでDIを使用)
デリゲート/イベントを使用すると、参照なしで子から親に通信できます。
上記のこの図では、Delegate / Eventを使用していません。Aのメソッドで関係のないビジネスロジックを実行するには、親コンポーネントB が親コンポーネントAの参照を持っている必要があります(高レベルの結合)。
このアプローチでは、コンポーネントBを使用するすべてのコンポーネントのすべての参照を配置する必要があります。:(
上記のこの図では、Delegate / Eventを使用しており、コンポーネントBはAを知っている必要はありません(低レベルの結合)。
また、コンポーネントBをアプリケーションのどこにでも使用できます。