デリゲートとイベントの違いは何ですか?どちらも実行可能な関数への参照を保持していませんか?
デリゲートとイベントの違いは何ですか?どちらも実行可能な関数への参照を保持していませんか?
回答:
イベント宣言は上の抽象化と保護の層を追加するデリゲートのインスタンス。この保護により、デリゲートのクライアントがデリゲートとその呼び出しリストをリセットできなくなり、呼び出しリストへのターゲットの追加または削除のみが許可されます。
違いを理解するには、この2つの例を見てください。
デリゲートの例(この場合、アクション-値を返さない一種のデリゲート)
public class Animal
{
public Action Run {get; set;}
public void RaiseEvent()
{
if (Run != null)
{
Run();
}
}
}
デリゲートを使用するには、次のようにする必要があります。
Animal animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();
このコードはうまく機能しますが、いくつかの弱点がある可能性があります。
たとえば、私がこれを書いた場合:
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;
コードの最後の行で、1つが足りないだけで以前の動作をオーバーライドしました+
(の=
代わりに使用しました+=
)
もう1つの弱点は、クラスを使用するすべてのクラスが、それを呼び出すだけでAnimal
レイズできるRaiseEvent
ことanimal.RaiseEvent()
です。
これらの弱点を回避するにevents
は、c#で使用できます。
あなたの動物クラスはこのように変化します:
public class ArgsSpecial : EventArgs
{
public ArgsSpecial (string val)
{
Operation=val;
}
public string Operation {get; set;}
}
public class Animal
{
// Empty delegate. In this way you are sure that value is always != null
// because no one outside of the class can change it.
public event EventHandler<ArgsSpecial> Run = delegate{}
public void RaiseEvent()
{
Run(this, new ArgsSpecial("Run faster"));
}
}
イベントを呼び出す
Animal animal= new Animal();
animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
違い:
ノート:
EventHandlerは、次のデリゲートとして宣言されています。
public delegate void EventHandler (object sender, EventArgs e)
(オブジェクト型の)送信者とイベント引数を受け取ります。静的メソッドからのものである場合、送信者はnullです。
を使用するこの例はEventHandler<ArgsSpecial>
、EventHandler
代わりにを使用して記述することもできます。
EventHandlerに関するドキュメントについては、こちらを参照してください
RaiseEvent
呼び出しメソッドがanimal
イベントを使用するコードののインスタンスにアクセスできる限り、誰も呼び出すことはできませんか?
animal.Run(this, new ArgsSpecial("Run faster");
ですか?
構文と操作のプロパティに加えて、意味上の違いもあります。
デリゲートは、概念的には関数テンプレートです。つまり、デリゲートの「タイプ」と見なされるために関数が従わなければならない契約を表します。
イベントは...まあ、イベントを表しています。彼らは何かが起こったときに誰かに警告することを意図していて、はい、彼らはデリゲートの定義に従っていますが、同じことではありません。
それらがまったく同じものであったとしても(構文的にもILコードにおいても)、意味的な違いは残ります。一般に、同じ方法で実装されている場合でも、2つの異なる概念には2つの異なる名前を付けることをお勧めします(これは、同じコードを2回使用するのが好きという意味ではありません)。
参照する別の良いリンクがあります。 http://csharpindepth.com/Articles/Chapter2/Events.aspx
簡単に言うと、記事からの取説-イベントはデリゲートをカプセル化したものです。
記事からの引用:
イベントがC#/。NETの概念として存在しなかったとします。別のクラスはどのようにイベントにサブスクライブしますか?3つのオプション:
パブリックデリゲート変数
プロパティに基づくデリゲート変数
AddXXXHandlerメソッドとRemoveXXXHandlerメソッドを持つデリゲート変数
オプション1は、私たちがパブリック変数を嫌う通常の理由すべてのために、明らかに恐ろしいです。
オプション2の方が少し優れていますが、サブスクライバーが互いに効果的にオーバーライドできるようになっています。新しいイベントハンドラを追加するのではなく、既存のイベントハンドラを置き換えます。さらに、プロパティを書き込む必要があります。
オプション3は基本的にイベントが提供するものですが、保証された規則(コンパイラーによって生成され、ILの追加のフラグによってサポートされます)と、フィールドのようなイベントが提供するセマンティクスに満足している場合は「無料」の実装です。イベントへのサブスクライブとサブスクライブ解除は、イベントハンドラーのリストへの任意のアクセスを許可せずにカプセル化されます。言語は、宣言とサブスクリプションの両方の構文を提供することにより、物事をより簡単にすることができます。
public Delegate
変数は「データ」を公開するだろうと彼が言っている場合、私の知る限り、OOPはDelegate
(オブジェクトでもメッセージでもない)のような概念については言及していませ ん。 、そして.NETはとにかくデリゲートをデータのようにほとんど扱いません。
AddXXXHandler
、private Delegate
変数を使用して独自のメソッドを作成することをお勧めします。この場合、ハンドラがすでに設定されているかどうかを確認し、適切に対応できます。これは、Delegate
すべてのハンドラーをクリアできるようにを保持しているオブジェクトが必要な場合にも適切な設定です(event
これを行う方法はありません)。
注:C#5.0 Unleashedにアクセスできる場合は、18章の「イベント」というタイトルの「デリゲートのプレーンな使用に関する制限」を読んで、2つの違いをよく理解してください。
簡単で具体的な例を示すことは常に助けになります。これがコミュニティ向けのものです。最初に、イベントを使用してデリゲートを単独で使用する方法を示します。次に、同じソリューションがのインスタンスでどのように機能するかを示しますEventHandler
。そして、最初の例で説明したことをしたくない理由を説明します。この投稿は、ジョン・スキートの記事に触発されました。
例1:パブリックデリゲートの使用
ドロップダウンボックスが1つしかないWinFormsアプリがあるとします。ドロップダウンはにバインドされていList<Person>
ます。Personには、Id、Name、NickName、HairColorのプロパティがあります。メインフォームには、そのユーザーのプロパティを表示するカスタムユーザーコントロールがあります。誰かがドロップダウンで人物を選択すると、ユーザーコントロールのラベルが更新され、選択した人物のプロパティが表示されます。
これがどのように機能するかです。これをまとめるのに役立つ3つのファイルがあります。
次に、各クラスに関連するコードを示します。
class Mediator
{
public delegate void PersonChangedDelegate(Person p); //delegate type definition
public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
{
if (PersonChangedDel != null)
{
PersonChangedDel(p);
}
}
}
ユーザーコントロールは次のとおりです。
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.PersonChangedDel += DetailView_PersonChanged;
}
void DetailView_PersonChanged(Person p)
{
BindData(p);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
最後に、Form1.csに次のコードを記述します。ここでは、デリゲートにサブスクライブされているコードを呼び出すOnPersonChangedを呼び出しています。
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}
OK。あなたはこの作業を取得する方法をそのですので、イベントを使用せずとばかりデリゲートを使用して。パブリックデリゲートをクラスに配置するだけで、静的またはシングルトンなどにできます。すごい。
しかし、しかし、私たちは、上で述べたようなことをしたくありません。ので、公共のフィールドが悪いです、多くの、多くの理由のために。それで私たちのオプションは何ですか?ジョン・スキートが説明するように、ここに私たちのオプションがあります:
PersonChangedDel = null
、他のすべてのサブスクリプションを削除してしまう可能性があります。ここで残っている他の問題は、ユーザーがデリゲートにアクセスできるため、呼び出しリストのターゲットを呼び出すことができるということです-外部ユーザーがイベントを発生させるときにアクセスできないようにする必要があります。この3番目のオプションは、基本的にイベントが提供するものです。EventHandlerを宣言すると、パブリックではなくプロパティとしてではなくデリゲートへのアクセスが提供されますが、このため、アクセサーの追加/削除のみを行うイベントを呼び出します。
同じプログラムの外観を見てみましょう。ただし、パブリックデリゲートの代わりにイベントを使用します(メディエーターもシングルトンに変更しました)。
例2:パブリックデリゲートの代わりにEventHandlerを使用する
調停者:
class Mediator
{
private static readonly Mediator _Instance = new Mediator();
private Mediator() { }
public static Mediator GetInstance()
{
return _Instance;
}
public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.
public void OnPersonChanged(object sender, Person p)
{
var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
if (personChangedDelegate != null)
{
personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
}
}
}
EventHandlerでF12を押すと、定義が追加の "sender"オブジェクトを含む一般化されたデリゲートであることがわかります。
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
ユーザーコントロール:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
}
void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
{
BindData(e.Person);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
最後に、Form1.csコードは次のとおりです。
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}
EventHandlerはパラメーターとしてEventArgsを必要としているため、このクラスを作成したのは、その中に1つのプロパティのみを含めた場合です。
class PersonChangedEventArgs
{
public Person Person { get; set; }
}
うまくいけば、イベントがある理由と、それらがデリゲートとどのように異なっているか(ただし機能的には同じか)について少しお分かりいただけると思います。
The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events
。の最新バージョンではMediator
、OnPersonChange
シングルトンへの参照があればいつでもを呼び出すことができます。たぶん、このMediator
アプローチはその特定の動作を妨げるものではなく、イベントバスに近いということを言及する必要があります。
デリゲートではなく、インターフェイス宣言でイベントを使用することもできます。
Action a { get; set; }
インターフェース定義内に含めることができます。
イベントと参加者の間での大きな誤解!!! デリゲートは、(例えば、TYPE指定class
、またはinterface
イベント(例えば等フィールド、プロパティ、等)の部材のちょうど一種であるのに対し、ないが)。また、他の種類のメンバーと同様に、イベントにもタイプがあります。ただし、イベントの場合は、デリゲートによってイベントのタイプを指定する必要があります。たとえば、インターフェイスで定義されたタイプのイベントを宣言することはできません。
結論として、次の観察を行うことができます。イベントのタイプはデリゲートによって定義されなければなりません。これはイベントとデリゲートの主な関係であり、セクションII.18 ECMA-335(CLI)パーティションIからVIのイベントの定義で説明されています。
一般的な使用法では、TypeSpec(存在する場合)は、シグネチャがイベントのfireメソッドに渡された引数と一致するデリゲートを識別します。
ただし、この事実は、イベントがバッキングデリゲートフィールドを使用することを意味するものではありません。実際、イベントでは、選択したさまざまなデータ構造タイプのバッキングフィールドを使用できます。C#でイベントを明示的に実装する場合、イベントハンドラーを格納する方法を自由に選択できます(イベントハンドラーはイベントのタイプのインスタンスであり、これは強制的にデリゲートタイプであることに注意してください---前の観察から))。ただし、これらのイベントハンドラー(デリゲートインスタンス)は、a List
やa などのデータ構造Dictionary
、またはバッキングデリゲートフィールドに格納することもできます。ただし、デリゲートフィールドの使用は必須ではないことを忘れないでください。
.netのイベントは、AddメソッドとRemoveメソッドの指定された組み合わせであり、どちらも特定の種類のデリゲートを想定しています。C#とvb.netはどちらも、イベントサブスクリプションを保持するデリゲートを定義するaddメソッドとremoveメソッドのコードを自動生成し、そのサブスクリプションデリゲートとの間で渡されたデリゲートを追加/削除できます。VB.netは、コードが自動生成され(RaiseEventステートメントを使用)、サブスクリプションリストが空でない場合にのみ呼び出されます。何らかの理由で、C#は後者を生成しません。
マルチキャストデリゲートを使用してイベントサブスクリプションを管理することは一般的ですが、それを行う唯一の方法ではないことに注意してください。パブリックの観点から、イベントを予定しているサブスクライバーは、イベントを受信することをオブジェクトに通知する方法を知る必要がありますが、パブリッシャーがイベントを発生させるために使用するメカニズムを知る必要はありません。また、.netでイベントデータ構造を定義した人はだれでも、それらを発生させるパブリックな方法があるはずだと考えていましたが、C#もvb.netもその機能を利用していません。
簡単な方法でイベントについて定義するには:
イベントは、2つの制限があるデリゲートへの参照です
上記の2つは、デリゲートの弱点であり、イベントで対処されます。フィドラーの違いを示す完全なコードサンプルは、https: //dotnetfiddle.net/5iR3fBです。
イベントとデリゲートのコメントを切り替え、値を呼び出す/割り当てるデリゲートにクライアントコードを割り当てて、違いを理解する
これがインラインコードです。
/*
This is working program in Visual Studio. It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
Event is an delegate reference with two restrictions for increased protection
1. Cannot be invoked directly
2. Cannot assign value to delegate reference directly
Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/
public class RoomTemperatureController
{
private int _roomTemperature = 25;//Default/Starting room Temperature
private bool _isAirConditionTurnedOn = false;//Default AC is Off
private bool _isHeatTurnedOn = false;//Default Heat is Off
private bool _tempSimulator = false;
public delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof)
// public OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above),
public event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above),
public RoomTemperatureController()
{
WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
}
private void InternalRoomTemperatuerHandler(int roomTemp)
{
System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");
}
//User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)
public bool TurnRoomTeperatureSimulator
{
set
{
_tempSimulator = value;
if (value)
{
SimulateRoomTemperature(); //Turn on Simulator
}
}
get { return _tempSimulator; }
}
public void TurnAirCondition(bool val)
{
_isAirConditionTurnedOn = val;
_isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
}
public void TurnHeat(bool val)
{
_isHeatTurnedOn = val;
_isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
}
public async void SimulateRoomTemperature()
{
while (_tempSimulator)
{
if (_isAirConditionTurnedOn)
_roomTemperature--;//Decrease Room Temperature if AC is turned On
if (_isHeatTurnedOn)
_roomTemperature++;//Decrease Room Temperature if AC is turned On
System.Console.WriteLine("Temperature :" + _roomTemperature);
if (WhenRoomTemperatureChange != null)
WhenRoomTemperatureChange(_roomTemperature);
System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
}
}
}
public class MySweetHome
{
RoomTemperatureController roomController = null;
public MySweetHome()
{
roomController = new RoomTemperatureController();
roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp;
//roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.
//roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
roomController.SimulateRoomTemperature();
System.Threading.Thread.Sleep(5000);
roomController.TurnAirCondition (true);
roomController.TurnRoomTeperatureSimulator = true;
}
public void TurnHeatOrACBasedOnTemp(int temp)
{
if (temp >= 30)
roomController.TurnAirCondition(true);
if (temp <= 15)
roomController.TurnHeat(true);
}
public static void Main(string []args)
{
MySweetHome home = new MySweetHome();
}
}