0

在下面的代码中,为了清晰起见,在第二类中使用 config 和 monitor 很简单。应该如何创建/注册/解决计时器?似乎它不能在第一类中完成,因为它需要第二类的构造函数参数值,但如果我理解正确,那么所有寄存器/解析都应该在第一类中进行。

using System;
using System.ServiceProcess;
using System.Threading;

using Autofac;

namespace MyServiceApp
{
    static class MyServiceAppMain
    {
        static void Main()
        {
            using (var container = InitContainer())
            {
                    container.Resolve<MyService>().Start();
            }
        }

        private static IContainer InitContainer()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Configuration>().As<IConfiguration>();
            builder.RegisterType<ServicesMonitor>();
            builder.RegisterType<MyService>();

            IContainer container = 
                builder.Build(Autofac.Builder.ContainerBuildOptions.None);

            return container;
        }
    }

    public partial class MyService : ServiceBase, IMyService
    {
        private Timer _processTimer;
        private int _intervalSize;
        private IConfiguration _config;
        private ServicesMonitor _monitor;

        public MyService(IConfiguration config, ServicesMonitor monitor)
        {
            InitializeComponent();

            _config = config;
            _monitor = monitor;

            _config.ReadAppConfig();
        }

        public void Start()
        {
            OnStart(null);
        }

        protected override void OnStart(string[] args)
        {
            _intervalSize = _config.IntervalMinutes * (60 * 1000);

            _processTimer = 
                new Timer(ProcessTimer_Elapsed, null, Timeout.Infinite, _intervalSize);
        }

        private void ProcessTimer_Elapsed(object sender)
        {
                _processTimer.Change(Timeout.Infinite, _intervalSize);
        }
    }
}
4

1 回答 1

0

我假设您的目标是可测试性或以其他方式抽象出Timer对象的创建。由于Timer实际上并未实现接口,因此您有几个选择。

但是,由于您正在修改Timer内部回调,您的情况变得复杂。从示例中,您实际上并没有进行任何更改 - 您设置了具有特定到期时间和回调周期的原始计时器......然后稍后将其设置为相同的东西。

选项一:做你已经在做的事

控制反转在有意义的地方非常有用,但是您不能真正“伪造”计时器或“换入”计时器以进行其他一些实现。如前所述,它没有实现任何ITimer接口或类似的东西。

如果你不必把它换掉……就让它发生吧。在测试期间交换配置值和东西,使其更适合您的测试并称之为好。

选项二:包装计时器

如果你需要在你的班级之外创建它,你需要把它包装在……东西中。然后你可以换掉那个包装器。

而且由于您正在修改Timer自身,因此您需要将一些功能包装进去。

public class SelfAdjustingTimer
{
  public Timer Timer { get; protected set; }
  public int Interval { get; private set; }
  public SelfAdjustingTimer(int interval)
  {
    this.Interval = interval;
    this.Timer = new Timer(Callback, null, Timeout.Infinite, interval);
  }
  private void Callback(object sender)
  {
    this.Timer.Change(Timeout.Infinite, this.Interval);
  }
}

如果您需要知道它何时发生或基于此做一些工作,您甚至可以添加一个事件或在回调期间引发的某些东西。

选项三:疯狂的 Lambda 处理程序

它可能会变得非常复杂,但您知道如何使用 lambda 表达式创建事件处理程序,例如...

someObj.TheEvent += (sender, args) => { /* Do Work */ };

假设您可以找到一种使用 lambda 表达式构建动态自引用回调并以这种方式创建计时器回调的方法。我以前做过类似的事情......它有点令人困惑。这就像生成 lambdas 的 lambdas 需要一段时间才能弄清楚(这就是为什么我不在这里把它交给你的原因)。

如果您不必修改回调中的计时器

...那么你有更多的选择。你可以...

  • 创建一个ITimerFactory并根据配置为您生成计时器。将其注入您的课程并使用它。(ITimerFactory它的实现是你必须创建的东西。它们不存在。`)
  • 像他们在 ASP.NET 中使用and所做的那样创建 aTimerBase和 a 。注入你的课堂,而不是更新一个. 对于 DI 注册,您可以在此处查找配置值并设置真实的.TimerWrapperHttpContextBaseHttpContextWrapperTimerBaseTimerTimerBaseTimer

...等等。所有选项都涉及抽象出具体Timer对象的概念,因为同样,它没有实现任何接口,也不是您甚至可以从中派生的东西。

如果是我......我可能会做你正在做的事情,尽可能地将代码隔离到一个较小的服务类中,该服务类可以进行所有交互Timer并具有更好的可模拟方法(例如,Timer用一些东西包装) 然后尽可能使用包装器进行测试。

于 2012-12-14T16:44:09.723 回答