0

次のコードでは、わかりやすくするために、2番目のクラスでconfigとmonitorを使用するのは簡単です。タイマーはどのように作成/登録/解決する必要がありますか?コンストラクターパラメーターとして2番目のクラスの値が必要なため、最初のクラスでは実行できないようですが、正しく理解していれば、すべてのレジスター/解決は最初のクラスで実行する必要があります。

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なります。この例からは、実際に変更を加えているようには見えません。特定の期限とコールバック期間を使用して元のタイマーを設定し、後で同じものに設定します。

オプション1:すでに行っていることを行う

制御の反転は、それが理にかなっている場合には優れていますが、タイマーを実際に「偽造」したり、他の実装のためにタイマーを「交換」したりすることはできません。前述のように、ITimerインターフェイスなどは実装されていません。

あなたがそれを交換する必要がないなら...ただそれを起こさせてください。テスト中に構成値などを交換して、テストに適したものにし、適切なものと呼びます。

オプション2:タイマーをラップする

クラスの外で作成する必要がある場合は、何かでラップする必要があります。次に、そのラッパーを交換できます。

また、それ自体を変更して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);
  }
}

イベントがいつ発生するかを知る必要がある場合、またはそれに基づいて何らかの作業を行う必要がある場合は、コールバック中に発生するイベントなどを追加することもできます。

オプション3:クレイジーラムダハンドラー

非常に複雑になる可能性がありますが、ラムダ式を使用してイベントハンドラーを作成する方法を知っています...

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

仮に、ラムダ式を使用して動的な自己参照コールバックを構築し、その方法でタイマーコールバックを作成する方法を見つけることができます。私は以前にこのようなことをしたことがあります...それは少し混乱します。これはラムダを生成するラムダのようなもので、理解するのに時間がかかります(これが、ここではそれを渡さない理由です)。

コールバックでタイマーを変更する必要がない場合

...その後、より多くのオプションがあります。あなたは出来る...

  • を作成し、ITimerFactory構成に基づいてタイマーを生成します。それをクラスに注入して使用します。(ITimerFactoryそしてその実装はあなたが作成しなければならないものです。それらは存在しません。`)
  • とを使用してASP.NETで行った方法でTimerBaseとを作成します。を新しくする代わりに、クラスに注入します。のDI登録の場合、ここで構成値を検索し、実際のを設定します。TimerWrapperHttpContextBaseHttpContextWrapperTimerBaseTimerTimerBaseTimer

...等々。すべてのオプションには、具体的なTimerオブジェクトの概念を抽象化することが含まれます。これも、インターフェイスを実装しておらず、派生することさえできないためです。

それが私だった場合...私はおそらくあなたがしていることをするでしょう、そのコードを可能な限り小さなサービスクラスに分離し、すべての相互作用を行いTimer、モック可能なより良いメソッドを持っています(例えば、Timer何かでラップする)次に、ラッパーを使用して可能な限りテストします。

于 2012-12-14T16:44:09.723 に答える