达到 DateTime 后执行方法的最有效方法

sty*_*ybl 3 c#

我有一堂课是这样的:

public sealed class Contract
{
    public bool isExpired { get; set; }
    public DateTime ExpirationDate { get; set; }
    public void MarkAsExpired()
    {
        this.isExpired = true;
        // Other stuff..
    }
}
Run Code Online (Sandbox Code Playgroud)

我想做的是:一旦ExpirationDate达到,MarkAsExpired应该被调用。如果程序关闭并且当它重新打开时ExpirationDate已经过去,同样的情况也会发生。如果isExpired这是真的,那么什么都不会发生。

合同是可以修改的,并且经常添加新合同。我的确切预期数量未知。

我已经想到了一种可以通过DateTime对象的通用扩展方法来完成此操作的方法:

var contracts = new List<Contract>();
foreach (var contract in contracts.Where(contract => !contract.isExpired))
{
    contract.ExpirationDate.ExecuteWhenPassed(() => contract.MarkAsExpired());
}
Run Code Online (Sandbox Code Playgroud)

问题在于编写扩展方法本身。我已经创建了一个工作解决方案:

static void ExecuteWhenPassed(this DateTime date, Action action)
{
    // Check if date has already passed
    if (date <= DateTime.Now)
    {
        action();
    }
    else
    {
        // Timer will fire when $date is reached
        var timer = new Timer { Interval = date.Subtract(DateTime.Now).TotalMilliseconds, AutoReset = false };
        timer.Elapsed += (s, e) => action();
        timer.Start();
    }
}
Run Code Online (Sandbox Code Playgroud)

而且效果很好。然而,我担心它的效率。具体来说,我担心为每个Contract实例创建单独的计时器所涉及的开销,其中可能有数百个实例尚未到期。

开销很大吗?如果是这样,处理这个问题的更有效方法是什么?

只要我处理过期的问题得到解决,我愿意接受完全不同的方法。

Eni*_*ity 6

我建议为此使用 Microsoft 的响应式框架(NuGet“System.Reactive”)。这变得超级容易。

这是代码:

List<Contract> contracts = new List<Contract>();

/* populate `contracts` here before `query` */  

IObservable<Contract> query =
    from i in Observable.Interval(TimeSpan.FromMinutes(1.0))
    from c in contracts
    where !c.isExpired
    where c.ExpirationDate <= DateTime.Now
    select c;

IDisposable subscription =
    query
        .Subscribe(c => c.MarkAsExpired());
Run Code Online (Sandbox Code Playgroud)

它所做的就是设置一个可观察的计时器 ( Observable.Interval(TimeSpan.FromMinutes(1.0))),每分钟触发一个值。然后,它每分钟都会过滤合约列表,仅筛选那些尚未到期且到期数据早于现在的合约。

然后在 中,subscription它只需要这些值的流并将它们标记为过期。

如果您想停止处理,只需致电subscription.Dispose()。这是一段非常简单的代码。

如果您在 Windows 窗体上运行此代码,您可以执行.ObserveOn(instanceOfFormOrControl)编组回到 UI 线程。在 WPF 上是这样.ObserveOnDispatcher()。该代码位于.Subscribe(...).

  • @stybl - `Observable.Using(() =&gt; new Context(), contex =&gt; /* return observable */)`。您可以将其用于任何一次性资源 - 然后 Rx 将为您管理一次性资源的生命周期。 (2认同)