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");
}

send函数中的receiver是我在使用函数时需要设置的参数,但是当我在send函数中添加参数时,比如:

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

然后它抛出一个错误。在我检查了 MSDN 后,它说 ElapsedEventArgs 仅适用于这些不会产生数据的功能。

我怎么解决这个问题?我的程序不是 windows.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) =>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;
}
于 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 回答