イベントとデリゲートの違い、およびそれぞれのアプリケーション[終了]


106

デリゲートよりもイベントを使用することの利点は、構文上の砂糖以外にはありません。誤解しているかもしれませんが、イベントはデリゲートのプレースホルダーにすぎないようです。

違いと、いつ使用するかを説明してください。長所と短所は何ですか?私たちのコードはイベントに根ざしているので、その根底に行きたいと思います。

イベントでデリゲートを使用するのはいつですか?両方の実際の経験を、たとえば量産コードで述べてください。


ええ、違いに頭を抱えるのは本当に大変でした。最初は同じように見え、同じように見えます
Robert Gould

1
この質問も参照してください。
Dimitri C.

1
2つのイベントとデリゲートの違いは、意見ではなく実際の問題です。これらの質問は、テクノロジーが解決する問題の違いを示しているため、それぞれのアプリケーションを求めています。これは意見の問題でもありません。誰がどちらが最適であるかは誰も尋ねなかったからです。この質問のどの部分も意見の問題ではなく、このステートメントも意見ではありません。私の考えでは。バッジを受け取りましたか?
Peter Wone

回答:


48

技術的な観点から、他の回答が違いに対処しています。

セマンティクスの観点からは、イベントは特定の条件が満たされたときにオブジェクトによって発生するアクションです。たとえば、私のStockクラスにはLimitというプロパティがあり、株価がLimitに達したときにイベントを発生させます。この通知はイベントを介して行われます。誰かがこのイベントを実際に気にかけてサブスクライブするかどうかは、オーナークラスの懸念を超えています。

デリゲートは、C / C ++用語のポインターに類似した構成を説明するより一般的な用語です。.Netのすべてのデリゲートはマルチキャストデリゲートです。セマンティクスの観点から、これらは一般的に一種の入力として使用されます。特に、これらはStrategy Patternを実装するのに最適な方法です。たとえば、オブジェクトのリストをソートする場合、2つのオブジェクトを比較する方法を実装に伝えるために、メソッドにコンパレータ戦略を提供できます。

私は製品コードで2つの方法を使用しました。特定のプロパティが満たされると、大量のデータオブジェクトが通知します。最も基本的な例では、プロパティが変更されるたびに、PropertyChangedイベントが発生します(INotifyPropertyChangedインターフェイスを参照)。コードでデリゲートを使用して、特定のオブジェクトを文字列に変換するさまざまな戦略を提供しました。この特定の例は、ユーザーに表示する特定のオブジェクト型の実装の栄光のあるToString()リストでした。


4
多分私は何かが足りないのですが、イベントハンドラは一種のデリゲートではありませんか?
Powerlord、2009

1
私の答えは、編集#1および#2の質問に対処します。使用の観点からの違い。この説明では、技術的な観点からは正しいとはいえ、両者は異なります。技術的な違いについては、他の回答をご覧ください。
Szymon Rozga、2009

3
「.Netのすべてのデリゲートはマルチキャストデリゲートです」?値を返すデリゲートでも?
Qwertie 2009年

5
はい。履歴については、msdn.microsoft.com/en-us/magazine/cc301816.aspxをご覧ください。チェックアウト:msdn.microsoft.com/en-us/library/system.delegate.aspx。それらが値を返す場合、返される値はチェーンの最後のデリゲートの評価です。
Szymon Rozga 2009年

デリゲートは、サブスクライバークラスで定義されたイベントハンドラーを指す参照型です。つまり、デリゲートは、(パブリッシャー内の)イベントとサブスクライバーで定義されたイベントハンドラーの間のリンクとして使用されます。アプリケーションでは、イベントをリッスンする必要のある複数のサブスクライバーが存在します。そのようなシナリオでは、デリゲートはパブリッシャーとサブスクライバーをリンクする効率的な方法を提供します。
josepainumkal 2017

54

キーワードeventは、マルチキャストデリゲートのスコープ修飾子です。これとマルチキャストデリゲートを宣言するだけの実際的な違いは次のとおりです。

  • eventインターフェースで使用できます。
  • マルチキャストデリゲートへの呼び出しアクセスは、宣言クラスに制限されています。動作は、デリゲートが呼び出し専用であるかのようです。割り当ての目的では、アクセスは明示的なアクセス修飾子(などpublic event)によって指定されたとおりです。

関心のある問題として、を適用+-てマルチキャストデリゲートに適用できます。これは、デリゲートをイベントに組み合わせて割り当てるための+=およびの-=構文の基本です。これらの3つのスニペットは同等です。

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B + C;

直接割り当てと組み合わせ割り当ての両方を示すサンプル2。

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B;
A += C;

サンプル3:より身近な構文。すべてのハンドラーを削除するためのnullの割り当てに精通している可能性があります。

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = null;
A += B;
A += C;

プロパティと同様に、イベントには完全な構文があり、誰も使用しません。この:

class myExample 
{
  internal EventHandler eh;

  public event EventHandler OnSubmit 
  { 
    add 
    {
      eh = Delegate.Combine(eh, value) as EventHandler;
    }
    remove
    {
      eh = Delegate.Remove(eh, value) as EventHandler;
    }
  }

  ...
}

... これとまったく同じです:

class myExample 
{
  public event EventHandler OnSubmit;
}

addおよびremoveメソッドは、VB.NETが使用する(演算子のオーバーロードなし)どちらかといえば堅固な構文でより目立ちます。


6
+「マルチキャストデリゲートへの呼び出しアクセスは宣言クラスに制限されています」-これは私にとってデリゲートとイベントの主な違いです。
RichardOD 2009年

2
もう1つの重要な違い(以下のitowlsonで説明)は、イベントに割り当ててすべてのイベントハンドラーのサブスクライブを解除することはできませんが、デリゲートを使用してそれを行うことができるということです。(ちなみに、あなたにとってはこれらすべての中で私にとって最も有用な答えでした)。
Roman Starkov

4
Googleやstackoverflowと同じくらい便利ですが、C#言語仕様の細部にまでこだわった詳細情報を入手でき、Microsoftから無料で公開されています。私はそれが
一見し

12

イベントは構文上の砂糖です。おいしいよ。イベントを見ると、どうしたらいいかわかります。デリゲートを見たとき、私はよくわかりません。

イベントとインターフェース(より多くの砂糖)を組み合わせると、美味しいスナックになります。デリゲートと純粋な仮想抽象クラスは食欲をそそります。


それは私もそれを見る方法です。私はより深く、より甘い説明を求めています:)

13
砂糖が多すぎると脂肪が1つになります... = P
エリックフォーブス

5

イベントはメタデータでそのようにマークされます。これにより、WindowsフォームやASP.NETデザイナーなどが、デリゲートタイプの単なるプロパティからイベントを区別し、適切なサポートを提供することができます(特に、[プロパティ]ウィンドウの[イベント]タブに表示されます)。

デリゲート型のプロパティとのもう1つの違いは、ユーザーはイベントハンドラーの追加と削除しかできないのに対し、デリゲート型のプロパティでは値を設定できることです。

someObj.SomeCallback = MyCallback;  // okay, replaces any existing callback
someObj.SomeEvent = MyHandler;  // not okay, must use += instead

これは、イベントサブスクライバーを分離するのに役立ちます。ハンドラーをイベントに追加できます。ハンドラーを同じイベントに追加でき、誤ってハンドラーを上書きすることはありません。


4

イベントは通常マルチキャストデリゲートで実装されますが、そのような方法で使用する必要はありません。クラスがイベントを公開する場合、それはクラスが2つのメソッドを公開することを意味します。それらの意味は、本質的には:

  1. ここにデリゲートがあります。何かおもしろいときに起動してください。
  2. ここにデリゲートがあります。できるだけ早くそれへのすべての参照を破棄する必要があります(呼び出さないでください)。

クラスが公開するイベントを処理する最も一般的な方法は、マルチキャストデリゲートを定義し、上記のメソッドに渡されるデリゲートを追加/削除することですが、そのように動作する必要はありません。残念ながら、イベントアーキテクチャは、代替アプローチをよりクリーンにするいくつかのことを実行できません(たとえば、サブスクリプションメソッドがMethodInvokerを返します。断然最も一般的なアプローチです。


4

違いを理解するには、この2つの例を見てください。

デリゲートの例(この場合のアクションは、値を返さない一種のデリゲートです)

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

デリゲートを使用するには、このようなことをする必要があります

Animale 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
    {
       public event EventHandler<ArgsSpecial> Run = delegate{} //empty delegate. In this way you are sure that value is always != null because no one outside of the class can change it

       public void RaiseEvent()
       {  
          Run(this, new ArgsSpecial("Run faster"));
       }
    }

イベントを呼び出す

 Animale animal= new Animal();
 animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
 animal.RaiseEvent();

違い:

  1. パブリックプロパティではなくパブリックフィールドを使用している(イベントを使用して、コンパイラが不要なアクセスからフィールドを保護する)
  2. イベントを直接割り当てることはできません。この場合、動作のオーバーライドで示した以前のエラーは実行できません。
  3. クラス外の誰もイベントを発生させることはできません。
  4. イベントはインターフェース宣言に含めることができますが、フィールドはできません

ノート

EventHandlerは、次のデリゲートとして宣言されています。

public delegate void EventHandler (object sender, EventArgs e)

送信者(オブジェクトタイプ)とイベント引数を受け取ります。静的メソッドからのものである場合、送信者はnullです。

EventHAndler代わりにこの例を使用することもできますEventHandler<ArgsSpecial>

EventHandlerに関するドキュメントについては、こちらを参照してください


3

Edit#1 イベントとvs.versaではいつデリゲートを使用しますか?両方の実際の経験を、たとえば量産コードで述べてください。

自分のAPIを設計するとき、メソッドまたはクラスのコンストラクターにパラメーターとして渡されるデリゲートを定義します。

  • だから、(例えばような方法は、単純な「テンプレートメソッド」パターンを実装することが可能PredicateAction代表者がネットジェネリックコレクションクラスに渡されます)
  • または、クラスが「コールバック」(通常、それを作成したクラスのメソッドへのコールバック)を実行できるようにします。

これらのデリゲートは、通常、実行時にオプションではありません(つまり、にすることはできませんnull)。

私はイベントを使用しない傾向があります。私は、使用イベントを行う場所が、私はそれらを使用し、必要に応じへのシグナル伝達事象を、ゼロ1、またはそれ以上のクライアントかもしれません興味がある、すなわち、それは、クラス(例えばという意味行ったときにSystem.Windows.Formクラス)が存在しなければならないが、任意のクライアントが持っているかどうかを実行しますイベントにイベントハンドラーを追加しました(フォームの「マウスダウン」イベントは存在しますが、外部クライアントがそのイベントにイベントハンドラーをインストールするかどうかはオプションです)。


2

技術的な理由はありませんが、UIスタイルのコード、つまりより高いレベルのコードでイベントを使用し、コードの奥にあるロジックにはデリゲートを使用します。私はあなたもどちらでも使用できると言いますが、私はこの使用パターンが論理的に健全であると思います。


編集:私は私が持っている使用パターンの違いはそれだと思う、私はイベントを無視することは完全に許容できると思うイベントはそれを無視します。それが私がUI、一種のJavaScript /ブラウザイベントスタイルに使用する理由です。ただし、デリゲートがある場合、誰かがデリゲートのタスクを処理することを本当に期待し、処理されない場合は例外をスローします。


UIでも偶数を使用するので、それについて詳しく説明してください。良い例は十分でしょう... ...ありがとう

1

イベントとデリゲートの違いは、私が思っていたよりもはるかに小さいです。件名に非常に短いYouTube動画を投稿しました:https : //www.youtube.com/watch?v=el-kKK-7SBU

お役に立てれば!


2
Stack Overflowへようこそ!これは理論的には質問に答えるかもしれませんが、望ましいでしょうますが、ここに答えの本質的な部分を含め、参照用のリンクを提供する。
GhostCat 2017

1

イベントの代わりにデリゲートのみを使用する場合、サブスクライバーは、以下の画像に示すように、デリゲート自体をclone()、invoke()する機会があります。それは正しくありません。

ここに画像の説明を入力してください

これが、b / wイベントとデリゲートの主な違いです。加入者には1つの権限しかありません。つまり、イベントを聞く

ConsoleLogクラスは、EventLogHandlerを介してログイベントをサブスクライブしています

public class ConsoleLog
{
    public ConsoleLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write on console : " + str);
    }
}

FileLogクラスは、EventLogHandlerを介してログイベントをサブスクライブしています

public class FileLog
{
    public FileLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write in File : " + str);
    }
}

操作クラスはログイベントを公開しています

public delegate void logDelegate(string str);
public class Operation
{
    public event logDelegate EventLogHandler;
    public Operation()
    {
        new FileLog(this);
        new ConsoleLog(this);
    }

    public void DoWork()
    {
        EventLogHandler.Invoke("somthing is working");
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.