如何延迟像QTimer :: singleShot这样的C#Action?

Dam*_*ien 5 c#

Qt有一个简洁的功能,可以使用Lambda进行定时操作.

使用单行代码延迟后可以执行操作:

    QTimer::singleShot(10, [=](){
        // do some stuff
    });
Run Code Online (Sandbox Code Playgroud)

虽然我没有在C#中找到相同的东西.


我得到的最接近的是

Timer timer = new Timer();
timer.Interval = 10;
timer.Elapsed += (tsender, args) => { 
  // do some stuff 
  timer.Stop();
};
timer.Start();
Run Code Online (Sandbox Code Playgroud)

但它远非(视觉上)清洁.

有没有更好的方法来实现这一目标?

用例是将串行线路上的数据发送到某些硬件,点击或按下按钮时,通常需要在几毫秒后发送命令和数据包.


具有辅助功能的解决方案:

    public void DelayTask(int timeMs, Action lambda)
    {
        System.Timers.Timer timer = new System.Timers.Timer();
        timer.Interval = timeMs;
        timer.Elapsed += (tsender, args) => { lambda.Invoke(); };
        timer.AutoReset = false;
        timer.Start();
    }
Run Code Online (Sandbox Code Playgroud)

叫做

DelayTask(10, () => /* doSomeStuff...*/ );
Run Code Online (Sandbox Code Playgroud)

Kzr*_*tof 5

我想到的最接近的东西就像你建议的辅助函数:

public static class DelayedAction
{
    public static Task RunAsync(TimeSpan delay, Action action)
    {
       return Task.Delay(delay).ContinueWith(t => action(), TaskScheduler.FromCurrentSynchronizationContext());
    }
}
Run Code Online (Sandbox Code Playgroud)

该课程的使用将与您对Qt的了解接近:

await DelayedAction.RunAsync(TimeSpan.FromSeconds(10), () => /* do stuff */);
Run Code Online (Sandbox Code Playgroud)

更新

由于在现有的SO提到的问题,ContinueWith不保留默认的同步上下文.

在当前的问题中,lambda正在更新一些UI控件,因此必须在UI线程上运行.

为此,调度程序必须在调用method ContinueWith(TaskScheduler.FromCurrentSynchronizationContext())时指定同步上下文,以确保可以进行此类更新.


Pan*_*vos 5

您应该使用System.Threading.Timer而不是 System.Timers.Timer。System.Timers.Timer是多线程计时器,旨在与桌面应用程序一起使用,这就是它继承自 Component 并需要通过属性进行配置的原因。

不过,System.Threading.Timer您可以使用单个构造函数调用创建单火计时器:

var timer= new Timer(_=>lambda(),null,timeMS,Timeout.Infinite);
Run Code Online (Sandbox Code Playgroud)

这个快速而肮脏的控制台应用程序:

static void Main(string[] args)
{
    var timeMS = 1000;
    var timer = new Timer(_ => Console.WriteLine("Peekaboo"), null, timeMS, Timeout.Infinite);
    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

Peekaboo即使主线程被阻塞,也会在 1 秒后打印ReadKey();