我有一堂课是这样的:
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实例创建单独的计时器所涉及的开销,其中可能有数百个实例尚未到期。
开销很大吗?如果是这样,处理这个问题的更有效方法是什么?
只要我处理过期的问题得到解决,我愿意接受完全不同的方法。
我建议为此使用 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(...).
| 归档时间: |
|
| 查看次数: |
2232 次 |
| 最近记录: |