18

私はsystem.Timers.Timerタイマーを作成するために使用しています。

public System.Timers.Timer timer = new System.Timers.Timer(200);
private void btnAutoSend_Click(object sender, EventArgs e)
{
    timer.Enabled = true;
    timer.Elapsed += new System.Timers.ElapsedEventHandler(send);
    timer.AutoReset = true;
}

public void send(object source, System.Timers.ElapsedEventArgs e)
{
    this.rtbMsg.AppendText("psyche-->" + receiver + ": hello\n");
}

送信関数のレシーバーは、関数を使用するときに設定する必要があるパラメーターですが、送信関数にパラメーターを追加すると、次のようになります。

public void send(object source, System.Timers.ElapsedEventArgs e,string receiver)

その後、エラーがスローされます。MSDN を確認したところ、ElapsedEventArgs はデータを生成しないこれらの関数でのみ使用できるとのことでした。

どうすればこの問題を解決できますか? 私のプログラムは windows.Form ではないので、.Form は使用できませんSystem.Windows.Forms.Timer

4

3 に答える 3

22

イベント ハンドラー コールバックに追加のパラメーターを渡すことはできません。それを呼び出すのはあなたではないためです。タイマーはそうです。それが要点です;-)

ただし、クロージャーを使用して同じ効果を簡単に実現できます。

private void btnAutoSend_Click(object sender, EventArgs e)
{
    timer.Elapsed += (timerSender, timerEvent) => send(timerSender, timerEvent, receiver);
    timer.AutoReset = true;
    timer.Enabled = true;
}

public void send(object source, System.Timers.ElapsedEventArgs e, string receiver)
{
    this.rtbMsg.AppendText("psyche-->" + receiver + ": hello\n");
}

現在、Elapsed ハンドラーは(timerSender, timerEvent) =>ラムダ アクションであり、変数を閉じ、ラムダがトリガーされるたびに追加のパラメーターを使用して手動でreceiver呼び出します。send

特定のケースでは、送信者または引数はまったく必要ないため、それらを転送する必要はありません。コードは次のようになります。

private void btnAutoSend_Click(object sender, EventArgs e)
{
    timer.Elapsed += (s_, e_) => OnTimerElapsed(receiver);
    timer.AutoReset = true;
    timer.Enabled = true;
}

private void OnTimerElapsed(string receiver)
{
    this.rtbMsg.AppendText("psyche-->" + receiver + ": hello\n");
}

これらすべてのオーバーヘッドについて疑問に思っている場合、それはごくわずかです。ラムダは単なるシンタックス シュガーであり、舞台裏の単純な関数です (イベント用にいくつかの自動デリゲート ラッピングがスローされます)。クロージャは、コンパイラによって生成されたクラスを使用して実装されますが、実際に大量のクロージャがない限り、コードの肥大化に気付くことはありません。

コメントで指摘されているように、コード内の UI 要素にアクセスしているようOnTimerElapsedです。Windows フォーム タイマーを使用していないため、コードが実行されるため、これを行うと例外が発生する可能性が高くなります。タイマーがイベントを発生させたときにたまたま実行されていたスレッド。Windows の UI コントロールは、それらを作成したスレッドからのみアクセスする必要があります。

手動で修正するためにいじることができますが、プロパティthis.Invokeを介してタイマーがイベントを適切なスレッドにマーシャリングする方が簡単です:SynchronizingObject

private void btnAutoSend_Click(object sender, EventArgs e)
{
    timer.SynchronizingObject = this;    // Assumes `this` implements ISynchronizeInvoke
    timer.Elapsed += (s_, e_) => OnTimerElapsed(receiver);
    timer.AutoReset = true;
    timer.Enabled = true;
}

最後に、別のコメントに促されて、クロージャーへの参照を保存して、後でイベントのサブスクライブを解除できる別の方法を次に示します。

private void btnAutoSend_Click(object sender, EventArgs e)
{
    timer.SynchronizingObject = this;    // Assumes `this` implements ISynchronizeInvoke
    ElapsedEventHandler onElapsed;
    onElapsed = (s_, e_) => {
        timer.Elapsed -= onElapsed;    // Clean up after firing
        OnTimerElapsed(receiver);
    };
    timer.Elapsed += onElapsed;
    timer.AutoReset = true;
    timer.Enabled = true;
}
于 2012-06-19T03:33:57.817 に答える
3

そのようなイベント ハンドラーに追加のパラメーターを渡すことはできません。

イベント ハンドラーでアクセスできるように、オブジェクト レベルの変数に値を格納します。

private string receiver;

public System.Timers.Timer timer = new System.Timers.Timer(200);
private void btnAutoSend_Click(object sender, EventArgs e)
{
    timer.Enabled = true;
    receiver = 'your val';
    timer.Elapsed += new System.Timers.ElapsedEventHandler(send);
    timer.AutoReset = true;
}

public void send(object source, System.Timers.ElapsedEventArgs e)
{
    this.rtbMsg.AppendText("psyche-->" + receiver + ": hello\n");
}
于 2012-06-19T03:37:20.050 に答える
2
public partial class Form2 : Form
{
    Timer timer = new Timer();
    public Form2()
    {
        InitializeComponent();
        timer.Tick += new EventHandler(timer_Tick); // Every time timer ticks, timer_Tick will be called
        timer.Interval = (10) * (1000);             // Timer will tick every 10 seconds
        timer.Start();                              // Start the timer
    }
    void timer_Tick(object sender, EventArgs e)
    {
        //MessageBox.Show("Tick");                    // Alert the user
        var time = DateTime.Now;
        label1.Text = $"{time.Hour} : {time.Minute} : {time.Seconds} : {time.Milliseconds}";
    }
    private void Form2_Load(object sender, EventArgs e)
    {

    }
}
于 2013-01-27T17:52:33.237 に答える