您不能将额外的参数传递给事件处理程序回调,因为您不是调用它的人——计时器是;这就是重点;-)
但是,您可以使用闭包轻松实现相同的效果:
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
变量并send
在触发 lambda 时使用额外参数手动调用。
在您的特定情况下,您根本不需要发件人或参数,因此无需转发它们。代码变为:
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 控件必须只能从创建它们的线程中访问。
您可以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;
}