你不能向事件处理程序回调函数传递额外的参数,因为调用它的不是你 -- 而是计时器;这就是整个问题的重点 ;-)
但是,你可以通过闭包轻松实现相同的效果:
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) =>
lambda 表达式,它封闭了 receiver
变量,并在每次触发 lambda 时手动调用 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");
}
如果你想知道这些的开销,那么它非常小。Lambda只是语法糖,在幕后是普通函数(对于事件部分会有一些自动委托包装)。闭包使用编译器生成的类来实现,但除非你真正有大量闭包,否则你不会注意到任何代码臃肿。
如评论中所指出的,您似乎正在
OnTimerElapsed
代码中访问UI元素--由于您没有使用Windows窗体计时器,在执行事件时代码将在计时器运行的任何线程上运行,并且在Windows中的UI控件必须仅从创建它们的线程访问。
您可以通过
SynchronizingObject
属性手动修复它,但最好让计时器为您将事件传递到正确的线程:
private void btnAutoSend_Click(object sender, EventArgs e)
{
timer.SynchronizingObject = this;
timer.Elapsed += (s_, e_) => OnTimerElapsed(receiver);
timer.AutoReset = true;
timer.Enabled = true;
}
最后,在另一条评论的提示下,这里提供另一种方法来存储对闭包的引用,以便稍后可以取消订阅事件:
private void btnAutoSend_Click(object sender, EventArgs e)
{
timer.SynchronizingObject = this;
ElapsedEventHandler onElapsed;
onElapsed = (s_, e_) => {
timer.Elapsed -= onElapsed;
OnTimerElapsed(receiver);
};
timer.Elapsed += onElapsed;
timer.AutoReset = true;
timer.Enabled = true;
}
System.Windows.Forms.Timer
,只需添加对该库的引用。 - annonymously