web-dev-qa-db-ja.com

イベントとデリゲートとそれぞれのアプリケーションの違い

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

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

いつイベントでデリゲートを使用しますか?実稼働コードなどで、両方の実世界での経験を述べてください。

105
Sasha

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

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

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

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

48
Szymon Rozga

キーワード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;
    }
  }

  ...
}

... does exactlyこれと同じ:

class myExample 
{
  public event EventHandler OnSubmit;
}

Addおよびremoveメソッドは、VB.NETが使用するかなり洗練された構文(オペレーターのオーバーロードなし)でより顕著です。

54
Peter Wone

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

イベントとインターフェース(砂糖の追加)を組み合わせることで、おいしい水まきができます。デリゲートと純粋な仮想抽象クラスは、食欲をそそります。

12
Sean

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

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

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

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

5
itowlson

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

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

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

4
supercat

Edit#1イベントとvs.ではなくいつデリゲートを使用しますか?実動コードなどで、両方の実世界での経験を述べてください。

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

  • メソッドが単純な「テンプレートメソッド」パターンを実装できるように(たとえば、PredicateおよびActionデリゲートが.Netジェネリックコレクションクラスに渡されるように)
  • または、クラスが「コールバック」(通常、それを作成したクラスのメソッドへのコールバック)を実行できるようにします。

これらはデリゲートは一般にオプションではありません実行時(つまり、nullであってはなりません)。

私はイベントを使用しない傾向があります。しかし、イベントを使用する場合は、オプションでイベントをゼロ、1つ、またはそれ以上可能性のあるクライアント、つまりクラス(例えば、System.Windows.Formクラス)が存在し、クライアントがそのイベントにイベントハンドラーを追加したかどうかにかかわらず実行する必要があります(たとえば、フォームの「マウスダウン」イベントは存在しますが、オプション外部クライアントがインストールに関心があるかどうかそのイベントへのイベントハンドラー)。

3
ChrisW

違いを理解するには、この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") ;
_

コードの最後の行で、以前の動作をオーバーライドするだけで、_+_が欠落しています(_+_の代わりに_+=_を使用しました)

別の弱点は、Animalクラスを使用するすべてのクラスが、animal.RaiseEvent()を呼び出すだけでRaiseEventを上げることができることです。

この弱点を回避するには、C#でeventsを使用できます。

あなたの動物のクラスはこのように変わります

_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<ArgsSpecial>_を使用するこの例の代わりにEventHAndlerを使用することもできます

参照 here EventHandlerに関するドキュメント

3
faby

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


編集:私が持っている使用パターンの違いは、イベントを無視することは完全に受け入れられると思うと思う、それらはフック/スタブであり、イベントについて知る必要がある場合、それらを聞いてくださいイベントはそれを無視します。だからこそ、Javascript/BrowserイベントスタイルのUIにそれらを使用します。ただし、デリゲートがある場合は、誰かがデリゲートのタスクを処理することを本当に期待し、処理されない場合は例外をスローします。

2
Robert Gould

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

お役に立てれば!

1

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

enter image description here

これが、イベントとデリゲートの主な違いです。サブスクライバーには権利が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");
    }
}
1
Narottam Goyal