(); // Write a function that counts the number of invocations networkInterfaceMock .Setup(n => n.Send(It.IsAny>())) .Callback(() => counter++); // Act: ... start the livebit service and wait for a good second ... var liveBit = new LiveBit(networkInterfaceMock.Object); await Task.Delay(TimeSpan.FromMilliseconds(1100)); // Assert: ... the mocked send method has been invoked once. Assert.Equal(1, counter);}To be honest whenever I see an await Task.Delay(someNumber) it never feels right. Usually, this is always some hack. I am not saying you should never do this, but in 95% of all cases, they are an indication of bad programming. However, what else can we do? “Shift time by one second!” - Sure, but how would you do that? Well, let me show you. First, let’s rewrite the timer function to use the Rx.Net approach:public class ReactiveLiveBit : IDisposable{ private readonly INetworkInterface _networkInterface; private readonly IDisposable _timer; public ReactiveLiveBit(INetworkInterface networkInterface) { _networkInterface = networkInterface; _timer = Observable .Interval(TimeSpan.FromSeconds(1)) .Subscribe(x => SendLiveBit()); } public void Dispose() { _timer.Dispose(); } private void SendLiveBit() { Console.WriteLine("Send Live Bit"); var liveBit = new byte[]{0xAA}; // Some imaginative payload _networkInterface.Send(liveBit); }}We can rerun our test, and it is still passing. However, the delay is still present in our test code. Looking at the definition of Rx.Net Scheduler, we see that it takes an IScheduler as an argument. This means that we can inject the time (ticker) into our scheduler - in other words, we can play timelords。所以,如果我们重写我们的构造函数,请访问我们的构造函数:public ReactiveLiveBit(INetworkInterface networkInterface, IScheduler scheduler = null){ var timerScheduler = scheduler ?? Scheduler.Default; _networkInterface = networkInterface; _timer = Observable .Interval(TimeSpan.FromSeconds(1), timerScheduler) .Subscribe(x => SendLiveBit());}We now pass in an optional parameter of IScheduler which is set to null by default. Moreover, if the parameter is not set, we use the Scheduler.Default. So if we do not inject a scheduler, the method uses the system clock and a second is a second. To test this, we can rerun our test at this point to find it still snoozing in the green. 使用Rx.Net测试时,有一个专用的NuGet package Microsoft.Reactive.Testing that provides some excellent helpers such as the TestScheduler.Using the TestScheduler we can rewrite our test code like this:[Fact]public void ReactiveLiveBit_WhenCreatedWithATestScheduler_TheSendMethodWillBeInvoked1TestSecond(){ // Arrange: Given a mock interface ... int counter = 0; // Using Moq //www.nuget.org/packages/Moq/ var networkInterfaceMock = new Mock(); // Write a function that counts the number of invocations networkInterfaceMock .Setup(n => n.Send(It.IsAny>())) .Callback(() => counter++); var testScheduler = new TestScheduler(); // Act: ... start the livebit service and wait for a good second ... var liveBit = new ReactiveLiveBit(networkInterfaceMock.Object, testScheduler); testScheduler.AdvanceBy(TimeSpan.FromSeconds(1).Ticks); // Assert: ... the mocked send method has been invoked once. Assert.Equal(1, counter);}Utilising the TestScheduler allows us to move forward in time with the AdvanceBy method. Forwarding the time will execute the timer code and reduce our test execution time to mere milliseconds.Conclusion所以你需要rx.net写定时器吗?不,你没有 - 然后再次你需要c#来编写应用程序而不是使用汇编程序吗?再也没有,你没有,但它有助于发货稳定和可维护的代码。请务必查看官方RX.NET网站,以查找RX.NET的更多资源。您可以在此小型样本中找到所有代码GitHub.HTH">

可测试的定时器,具有.NET的反应扩展

大本钟看法在晚上与运输的红绿灯。

反应延伸 ( rx.net. )于2009年发布,因此今年正在庆祝这是10周年纪念日。反应扩展是众所周知的基于事件的系统的方法方法。但是,它还具有许多其他有用的功能,例如计时器!我猜博客的标题可能已经给出了这一点 - 仍然可以让我告诉你为什么Rx.Net为您提供标准.NET定时器的替代方案。

通常,定时器为两种情况提供服务。延迟执行某个逻辑的执行,直到指定的稍后时间或定期执行一些逻辑。例如,在网络堆栈中,常常每n秒发送心跳/保持活动消息,让对应于通信伙伴仍然存在,并且连接应保持打开。我们可以实施如此一般计时器:

public class LiveBit : IDisposable
{
    private readonly INetworkInterface _networkInterface;
    private readonly Timer _timer;

    public LiveBit(INetworkInterface networkInterface)
    {
        _networkInterface = networkInterface;
        _timer = new Timer(1000);
        _timer.Elapsed += SendLiveBit;
        _timer.AutoReset = true;
        _timer.Enabled = true;
    }

    public void Dispose()
    {
        _timer.Dispose();
    }

    private void SendLiveBit(object sender, ElapsedEventArgs e)
    {
        Console.WriteLine("Send Live Bit");
        var liveBit = new byte[]{0xAA}; // Some imaginative payload
        _networkInterface.Send(liveBit);
    }
}

代码仅使用.NET库实现,不使用Rx.Net。那么我们为什么要忘记替换这段代码?嗯,有人怎么去测试这个代码?我们可以写一个看起来像这样的测试:

[Fact]
public async Task LiveBit_WhenCreated_TheSendMethodWillBeInvokedAfter1Second()
{
    // Arrange: Given a mock interface ...
    int counter = 0;
    // Using Moq //www.nuget.org/packages/Moq/
    var networkInterfaceMock = new Mock();
    // Write a function that counts the number of invocations
    networkInterfaceMock
        .Setup(n => n.Send(It.IsAny>()))
        .Callback(() => counter++);
    // Act: ... start the livebit service and wait for a good second ...
    var liveBit = new LiveBit(networkInterfaceMock.Object);
    await Task.Delay(TimeSpan.FromMilliseconds(1100));
    // Assert: ... the mocked send method has been invoked once.
    Assert.Equal(1, counter);
}

To be honest whenever I see an await Task.Delay(someNumber) it never feels right. Usually, this is always some hack. I am not saying you should never do this, but in 95% of all cases, they are an indication of bad programming. However, what else can we do? “Shift time by one second!” - Sure, but how would you do that? Well, let me show you. First, let’s rewrite the timer function to use the Rx.Net approach:

public class ReactiveLiveBit : IDisposable
{
    private readonly INetworkInterface _networkInterface;
    private readonly IDisposable _timer;

    public ReactiveLiveBit(INetworkInterface networkInterface)
    {
        _networkInterface = networkInterface;
        _timer = Observable
            .Interval(TimeSpan.FromSeconds(1))
            .Subscribe(x => SendLiveBit());
    }

    public void Dispose()
    {
        _timer.Dispose();
    }

    private void SendLiveBit()
    {
        Console.WriteLine("Send Live Bit");
        var liveBit = new byte[]{0xAA}; // Some imaginative payload
        _networkInterface.Send(liveBit);
    }
}

We can rerun our test, and it is still passing. However, the delay is still present in our test code. Looking at the definition of Rx.Net Scheduler, we see that it takes an IScheduler as an argument. This means that we can inject the time (ticker) into our scheduler - in other words, we can play 时间表 。所以,如果我们重写我们的构造函数,请访问我们的构造函数:

public ReactiveLiveBit(INetworkInterface networkInterface, IScheduler scheduler = null)
{
    var timerScheduler = scheduler ?? Scheduler.Default;
    _networkInterface = networkInterface;
    _timer = Observable
        .Interval(TimeSpan.FromSeconds(1), timerScheduler)
        .Subscribe(x => SendLiveBit());
}

We now pass in an optional parameter of IScheduler which is set to null by default. Moreover, if the parameter is not set, we use the Scheduler.Default. So if we do not inject a scheduler, the method uses the system clock and a second is a second. To test this, we can rerun our test at this point to find it still snoozing in the green.

使用Rx.Net测试时,有一个专用的 尼古特包 Microsoft.Reactive.Testing that provides some excellent helpers such as the TestScheduler.

Using the TestScheduler we can rewrite our test code like this:

[Fact]
public void ReactiveLiveBit_WhenCreatedWithATestScheduler_TheSendMethodWillBeInvoked1TestSecond()
{
    // Arrange: Given a mock interface ...
    int counter = 0;
    // Using Moq //www.nuget.org/packages/Moq/
    var networkInterfaceMock = new Mock();
    // Write a function that counts the number of invocations
    networkInterfaceMock
        .Setup(n => n.Send(It.IsAny>()))
        .Callback(() => counter++);
    var testScheduler = new TestScheduler();
    // Act: ... start the livebit service and wait for a good second ...
    var liveBit = new ReactiveLiveBit(networkInterfaceMock.Object, testScheduler);
    testScheduler.AdvanceBy(TimeSpan.FromSeconds(1).Ticks);
    // Assert: ... the mocked send method has been invoked once.
    Assert.Equal(1, counter);
}

Utilising the TestScheduler allows us to move forward in time with the AdvanceBy method. Forwarding the time will execute the timer code and reduce our test execution time to mere milliseconds.

结论

所以你需要rx.net写定时器吗?不,你没有 - 然后再次你需要c#来编写应用程序而不是使用汇编程序吗?再也没有,你没有,但它有助于发货稳定和可维护的代码。请务必查看官方RX.NET网站,以查找RX.NET的更多资源。

您可以在此小型样本中找到所有代码 GitHub. .

Hth.

更新: