12

現在、デリゲートを使用して C# でイベントを理解し、実装するのに苦労しています。私はJavaのやり方に慣れています:

  1. 多数のメソッド定義を含むリスナー タイプのインターフェイスを定義します。
  2. リスナーで定義されたすべてのイベントに興味がない場合は、そのインターフェイスのアダプター クラスを定義して作業を容易にします。
  3. イベントを発生させるクラスで Add、Remove、および Get[] メソッドを定義します。
  4. 保護された fire メソッドを定義して、追加されたリスナーのリストをループし、正しいメソッドを呼び出すという汚い作業を行います

これは私が理解しています(そして好きです!)-これをc#でもまったく同じようにできることは知っていますが、c#用の新しい(より良い?)システムが整っているようです。C# でのデリゲートとイベントの使用を説明する数え切れないほどのチュートリアルを読んだ後、何が起こっているのかを本当に理解するにはまだ近づいていません :S


要するに、次のメソッドについて、イベント システムを c# で実装するにはどうすればよいでしょうか。

void computerStarted(Computer computer);
void computerStopped(Computer computer);
void computerReset(Computer computer);
void computerError(Computer computer, Exception error);

^ 上記のメソッドは、私がかつて作成した Java アプリケーションから取得したもので、これを c# に移植しようとしています。

大変感謝します!

4

9 に答える 9

17

エラーを示すための新しいEventArgsベースのクラスとともに、4つのイベントとそれらを発生させるメソッドを作成します。

public class ExceptionEventArgs : EventArgs
{
    private readonly Exception error;

    public ExceptionEventArgs(Exception error)
    {
         this.error = error;
    }

    public Error
    {
         get { return error; }
    }
}

public class Computer
{
    public event EventHandler Started = delegate{};
    public event EventHandler Stopped = delegate{};
    public event EventHandler Reset = delegate{};
    public event EventHandler<ExceptionEventArgs> Error = delegate{};

    protected void OnStarted()
    {
        Started(this, EventArgs.Empty);
    }

    protected void OnStopped()
    {
        Stopped(this, EventArgs.Empty);
    }

    protected void OnReset()
    {
        Reset(this, EventArgs.Empty);
    }

    protected void OnError(Exception e)
    {
        Error(this, new ExceptionEventArgs(e));
    }
}

次に、クラスはメソッドまたは無名関数のいずれかを使用してイベントをサブスクライブします。

someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{ 
    Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");

上記について注意すべき点がいくつかあります。

  • OnXXXメソッドは、派生クラスがイベントを発生させることができるように保護されています。これは必ずしも必要ではありません-適切と思われる方法で実行してください。
  • 各イベント宣言のdelegate{}部分は、nullチェックを実行する必要を回避するための単なるトリックです。各イベントにno-opイベントハンドラーをサブスクライブしています
  • イベント宣言はフィールドのようなイベントです。実際に作成されているのは、変数イベントの両方です。クラス内に変数が表示されます。クラスの外では、イベントが表示されます。

イベントの詳細については、私のイベント/委任の記事を参照してください。

于 2008-10-08T05:29:53.963 に答える
5

そのために単一のデリゲートを定義する必要があります

public delegate void ComputerEvent(object sender, ComputerEventArgs e);

ComputerEventArgsは、次のように定義されます。

public class ComputerEventArgs : EventArgs
{
    // TODO wrap in properties
    public Computer computer;
    public Exception error;

    public ComputerEventArgs(Computer aComputer, Exception anError)
    {
        computer = aComputer;
        error = anError;
    }

    public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
    {
    }
}

イベントを発生させるクラスには、次のものがあります。

public YourClass
{
    ...
    public event ComputerEvent ComputerStarted;
    public event ComputerEvent ComputerStopped;
    public event ComputerEvent ComputerReset;
    public event ComputerEvent ComputerError;
    ...
}

これは、イベントにハンドラーを割り当てる方法です。

YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);

ハンドラーは次のとおりです。

private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
   // do your thing.
}
于 2008-10-08T05:20:29.817 に答える
4

主な違いは、C#ではイベントがインターフェイスベースではないことです。代わりに、イベントパブリッシャーは、関数ポインターと考えることができるデリゲートを宣言します(ただし、まったく同じではありません:-))。次に、サブスクライバーはイベントプロトタイプを通常のメソッドとして実装し、デリゲートの新しいインスタンスをパブリッシャーのイベントハンドラーチェーンに追加します。デリゲートイベントの詳細をご覧ください。

ここで、C#イベントとJavaイベントの簡単な比較を読むこともできます。

于 2008-10-08T05:22:32.043 に答える
2

まず、.Netには、通常イベントに使用される標準のメソッドシグネチャがあります。言語では、あらゆる種類のメソッドシグネチャをイベントに使用できます。また、規則に欠陥があると信じている専門家もいますが(私はほとんど同意します)、それがその通りであり、この例ではそれに従います。

  1. イベントのパラメーター(EventArgsから派生)を含むクラスを作成します。
パブリッククラスComputerEventArgs:EventArgs
{{
  コンピューターコンピューター;
  //コンストラクタ、プロパティなど
}
  1. イベントを発生させるクラスで公開イベントを作成します。
    classComputerEventGenerator//ひどい名前を選んだところで。
    {{
      パブリックイベントEventHandler<ComputerEventArgs>ComputerStarted;
      パブリックイベントEventHandler<ComputerEventArgs>ComputerStopped;
      パブリックイベントEventHandler<ComputerEventArgs>ComputerReset;
    ..。
    }
  1. イベントを呼び出します。
    クラスComputerEventGenerator
    {{
    ..。
      private void OnComputerStarted(コンピューターコンピューター)
      {{
        EventHandler <ComputerEventArgs> temp = ComputerStarted;
        if(temp!= null)temp(this、new ComputerEventArgs(computer)); //イベントが静的な場合は、「this」をnullに置き換えます
      }
     }
  1. イベントのハンドラーをアタッチします。
    void OnLoad()
    {{
      ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator();
      computerEventGenerator.ComputerStarted + = new EventHandler <ComputerEventArgs>(ComputerEventGenerator_ComputerStarted);
    }
  1. アタッチしたばかりのハンドラーを作成します(主にVSでTabキーを押します)。
    private void ComputerEventGenerator_ComputerStarted(object sender、ComputerEventArgs args)
    {{
      if(args.Computer.Name == "HAL9000")
         ShutItDownNow(args.Computer);
    }
  1. 完了したら、ハンドラーをデタッチすることを忘れないでください。(これを忘れると、C#でのメモリリークの最大の原因になります!)
    void OnClose()
    {{
      ComputerEventGenerator.ComputerStarted-= ComputerEventGenerator_ComputerStarted;
    }

以上です!

編集:私の番号の付いたポイントがすべて「1」として表示される理由を正直に理解できません。私はコンピューターが嫌いです。

于 2008-10-08T05:32:34.770 に答える
1

あなたがやりたいことをするためのいくつかの方法があります。最も直接的な方法は、ホスティングクラスの各イベントのデリゲートを定義することです。

public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
    if (ComputerStarted != null)
    {
        ComputerStarted.Invoke(computer);
    }
}
protected void someMethod()
{
    //...
    computer.Started = true;  //or whatever
    OnComputerStarted(computer);
    //...
}

どのオブジェクトも、次の方法でこのイベントを「リッスン」できます。

Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
    this.ComputerStartedHandler);

protected void ComputerStartedHandler(Computer computer)
{
    //do something
}

これを行うための「推奨される標準的な方法」は、コンピューター(および古い/新しい状態と例外)値を保持するEventArgsのサブクラスを定義し、4つのデリゲートを1つに減らすことです。この場合、それはよりクリーンな解決策になります、特に。後で拡張する場合に備えて、コンピューターの状態の列挙型を使用します。ただし、基本的な手法は同じです。

  • デリゲートは、イベントハンドラー/リスナーの署名/インターフェースを定義します
  • イベントデータメンバーは「リスナー」のリストです

リスナーは、+=ではなく-=構文を使用して削除されます

于 2008-10-08T05:23:59.823 に答える
1

C#では、イベントはデリゲートです。これらはC/C ++の関数ポインタと同じように動作しますが、System.Delegateから派生した実際のクラスです。

この場合、Computerオブジェクトを渡すためのカスタムEventArgsクラスを作成します。

public class ComputerEventArgs : EventArgs
{
  private Computer _computer;

  public ComputerEventArgs(Computer computer) {
    _computer = computer;
  }

  public Computer Computer { get { return _computer; } }
}

次に、プロデューサーからのイベントを公開します。

public class ComputerEventProducer
{
  public event EventHandler<ComputerEventArgs> Started;
  public event EventHandler<ComputerEventArgs> Stopped;
  public event EventHandler<ComputerEventArgs> Reset;
  public event EventHandler<ComputerEventArgs> Error;

  /*
  // Invokes the Started event */
  private void OnStarted(Computer computer) {
    if( Started != null ) {
      Started(this, new ComputerEventArgs(computer));
    }
  }

  // Add OnStopped, OnReset and OnError

}

次に、イベントのコンシューマーは、ハンドラー関数をコンシューマー上の各イベントにバインドします。

public class ComputerEventConsumer
{
  public void ComputerEventConsumer(ComputerEventProducer producer) {
    producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
    // Add other event handlers
  }

  private void ComputerStarted(object sender, ComputerEventArgs e) {
  }
}

ComputerEventProducerがOnStartedを呼び出すと、Startedイベントが呼び出され、ComputerEventConsumer.ComputerStartedメソッドが呼び出されます。

于 2008-10-08T05:24:52.990 に答える
0

デリゲートは関数シグネチャを宣言し、クラスのイベントとして使用されると、登録された呼び出しターゲットのコレクションとしても機能します。イベントの += および -= 構文は、ターゲットをリストに追加するために使用されます。

イベントとして使用される次のデリゲートがあるとします。

// arguments for events
public class ComputerEventArgs : EventArgs
{
    public Computer Computer { get; set; }
}

public class ComputerErrorEventArgs : ComputerEventArgs
{
    public Exception Error  { get; set; }
}

// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);

public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);

// component that raises events
public class Thing
{
    public event ComputerEventHandler Started;
    public event ComputerEventHandler Stopped;
    public event ComputerEventHandler Reset;
    public event ComputerErrorEventHandler Error;
}

これらのイベントをサブスクライブするには、次のようにします。

class Program
{
    static void Main(string[] args)
    {
        var thing = new Thing();
        thing.Started += thing_Started;
    }

    static void thing_Started(object sender, ComputerEventArgs e)
    {
        throw new NotImplementedException();
    }
}

引数は何でもかまいませんが、オブジェクトの送信者と EventArgs e は非常に一貫して使用される規則です。+= thing_started は、最初にターゲット メソッドを指すデリゲートのインスタンスを作成し、次にそれをイベントに追加します。

コンポーネント自体では、通常、イベントを発生させるメソッドを追加します。

public class Thing
{
    public event ComputerEventHandler Started;

    public void OnStarted(Computer computer)
    {
        if (Started != null)
            Started(this, new ComputerEventArgs {Computer = computer});
    }
}

イベントにデリゲートが追加されていない場合は、null をテストする必要があります。ただし、メソッド呼び出しを行うと、追加されたすべてのデリゲートが呼び出されます。これが、イベントの戻り値の型が void である理由です。単一の戻り値はありません。したがって、情報をフィードバックするには、イベント ハンドラーが変更する EventArgs にプロパティを設定します。

もう 1 つの改良点は、引数の型ごとに具体的なデリゲートを宣言するのではなく、汎用の EventHandler デリゲートを使用することです。

public class Thing
{
    public event EventHandler<ComputerEventArgs> Started;
    public event EventHandler<ComputerEventArgs> Stopped;
    public event EventHandler<ComputerEventArgs> Reset;
    public event EventHandler<ComputerErrorEventArgs> Error;
}
于 2008-10-08T05:47:34.630 に答える
0

回答ありがとうございます。最後に、私は何が起こっているのかを理解し始めています。たったひとつ; 各イベントの引数の数/タイプが異なる場合、それを処理するために別の :: EventArgs クラスを作成する必要があるようです。

public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);

これは、イベントを処理するために 3 つのクラスが必要になる!? (まあ、2 つのカスタムと、デフォルトの EventArgs.Empty クラスを使用する 1 つ)

乾杯!

于 2008-10-08T14:33:40.043 に答える
0

わかりました、最終的な説明です!:これは、これらのイベントを実装するためにコード的に実行できる最善の方法ですか?

   public class Computer {

        public event EventHandler Started;

        public event EventHandler Stopped;

        public event EventHandler Reset;

        public event EventHandler<BreakPointEvent> BreakPointHit;

        public event EventHandler<ExceptionEvent> Error;

        public Computer() {
            Started = delegate { };
            Stopped = delegate { };
            Reset = delegate { };
            BreakPointHit = delegate { };
            Error = delegate { };
        }

        protected void OnStarted() {
            Started(this, EventArgs.Empty);
        }

        protected void OnStopped() {
            Stopped(this, EventArgs.Empty);
        }

        protected void OnReset() {
            Reset(this, EventArgs.Empty);
        }

        protected void OnBreakPointHit(int breakPoint) {
            BreakPointHit(this, new BreakPointEvent(breakPoint));
        }

        protected void OnError(System.Exception exception) {
            Error(this, new ExceptionEvent(exception));
        }
    }
}
于 2008-10-08T16:36:56.533 に答える