代码之家  ›  专栏  ›  技术社区  ›  BobTheBuilder

如何设计一个类来接收具有未知数量参数的委托?

  •  4
  • BobTheBuilder  · 技术社区  · 17 年前

    我不断发现自己必须编写定时windows服务,在队列外轮询或运行定时进程。因此,我有一个相当健壮的模板来执行此操作,但我发现每次我编写服务来执行此操作时,我都从同一个模板开始,然后在其中编写流程。

    模板的核心只是一个计时器,它在初始化和启动流程时停止,然后在流程完成后重新启动计时器。然后,我将我的流程方法和任何依赖项直接添加到模板中。

    有人知道怎么做吗?我已经研究过依赖注入,并且经常使用它来注入数据存储连接之类的东西?是否有方法将具有未知数量/类型参数的委托注入到类中?我看得不对吗?

    TimedProcess.Template.cs

    using System;
    using System.Timers;
    
    public partial class TimedProcess : IDisposable
    {
        private Timer timer;
    
        public bool InProcess { get; protected set; }
    
        public bool Running
        {
            get
            {
                if (timer == null)
                    return false;
    
                return timer.Enabled;
            }
        }
    
        private void InitTimer(int interval)
        {
            if (timer == null)
            {
                timer = new Timer();
                timer.Elapsed += TimerElapsed;
            }
            Interval = interval;
        }
    
        public void InitExecuteProcess()
        {
            timer.Stop();
            InProcess = true;
            RunProcess();
            InProcess = false;
            timer.Start();   
        }
    
        public void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            InitExecuteProcess();
        }
    
        public void Start()
        {
            if (timer != null && timer.Interval > 0)
                timer.Start();
        }
    
        public void Start(int interval)
        {
            InitTimer(interval);
            Start();
        }
    
        public void Stop()
        {
            timer.Stop();
        }
    
        public TimedProcess()
            : this(0)
        {
        }
    
        public TimedProcess(int interval)
        {
            if (interval > 0)
                InitTimer(interval);
        }
    
        private disposed = false;
        public Dispose(bool disposing)
        {
            if (disposed || !disposing)
                return;
    
            timer.Dispose();
    
            disposed = true;
        }
    
        public Dispose()
        {
            Dispose(true);
        }
    
        ~TimedProcess()
        {
            Dispose(false);
        }
    }
    

    using System;
    
    public partial class TimedProcess
    {
        public void RunProcess()
        {
            //Hook process to run in here
        }
    }
    

    因此,我希望对其进行修改,以便我的Windows服务生成一个新的TimedProcess,并向其注入需要运行的进程,从而从我的Windows服务中完全删除TimedProcess代码,并使其引用DLL。

    编辑 那个 作为构造器中的一个动作,这简化了我所期待的一切:

    [为简洁起见简化]

    public class TimedProcess
    {
        Action RunProcess;
        Timer timer = new Timer();
    
        private void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            if (RunProcess != null)
                RunProcess();
        }
    
        public TimedProcess(Action action, int interval)
        {
            timer.Interval = interval;
            RunProcess = action;
            timer.Start();
        }
    }
    
    6 回复  |  直到 17 年前
        1
  •  3
  •   Marc Gravell    17 年前

    这里的一种方法是使用捕获的变量,以便所有代理 基本上 Action 或许 Func<T> -并通过匿名方法、捕获的变量等将其余部分留给调用方。

    DoStuff( () => DoSomethingInteresting("abc", 123) );
    

    (注意:注意异步/捕获-通常是一个糟糕的组合)

    哪里 DoStuff 行动 . 然后当您调用 行动 参数是自动添加的,等等。在一些RPC库代码中,我采用了另一种方法,使用 Expression

    Invoke(Expression<Action<TService>> action) {...}
    Invoke<TValue>(Expression<Func<TService,TValue>> func) {...}
    

    proxy.Invoke(svc => svc.CancelOrder(orderNumber));
    

    如果 我们有一个接口的实现——除非我们从来没有真正实现过;相反,我们拉着 here ,或代码可用 here

        2
  •  1
  •   Reed Copsey    17 年前

    让委托接受一组未知参数的一种方法是传递一个对象数组。然后可以使用数组的长度作为参数的数量,因为任何类型都可以转换为对象,所以可以传递任何内容。

        3
  •  1
  •   Lucero    17 年前

    您可以使用不带参数的委托来表示“服务调用”:

    ThreadStart taskToPerform = delegate() { // do your stuff here
       YourService.Call(X, Y, Z);
    };
    Template.AddProcess(taskToPerform);
    
        4
  •  1
  •   Troy Howard    17 年前

    两者都使用已经讨论过的包装委托方法。一个使用“params”,另一个使用泛型。两者都避免了“异步/捕获”问题。事实上,这与事件的实现方式非常相似。

    很抱歉,这都是一个很长的代码块。我将其分为三个子空间:

    • FakeDomain(例如保存模拟)
    • UsingParams(保存使用params关键字的实现)
    • 使用泛型(保存使用泛型的实现)

    using System;
    using System.Timers;
    using StackOverflow.Answers.InjectTaskWithVaryingParameters.FakeDomain;
    
    namespace StackOverflow.Answers.InjectTaskWithVaryingParameters
    {
        public static class ExampleUsage
        {
            public static void Example1()
            {
                // using timed task runner with no parameters
    
                var timedProcess = new UsingParams.TimedProcess(300, FakeWork.NoParameters);
    
                var timedProcess2 = new UsingGenerics.TimedProcess(300, FakeWork.NoParameters);
            }
    
            public static void Example2()
            {
                // using timed task runner with a single typed parameter 
    
                var timedProcess =
                    new UsingParams.TimedProcess(300,
                        p => FakeWork.SingleParameter((string)p[0]),
                        "test"
                    );
    
                var timedProcess2 =
                    new UsingGenerics.TimedProcess<StringParameter>(
                            300,
                            p => FakeWork.SingleParameter(p.Name),
                            new StringParameter()
                            {
                                Name = "test"
                            }
                    );
            }
    
            public static void Example3()
            {
                // using timed task runner with a bunch of variously typed parameters 
    
                var timedProcess =
                    new UsingParams.TimedProcess(300,
                        p => FakeWork.LotsOfParameters(
                            (string)p[0],
                            (DateTime)p[1],
                            (int)p[2]),
                        "test",
                        DateTime.Now,
                        123
                    );
    
                var timedProcess2 =
                    new UsingGenerics.TimedProcess<LotsOfParameters>(
                        300,
                        p => FakeWork.LotsOfParameters(
                            p.Name,
                            p.Date,
                            p.Count),
                        new LotsOfParameters()
                        {
                            Name = "test",
                            Date = DateTime.Now,
                            Count = 123
                        }
                    );
            }
        }
    
        /* 
         * Some mock objects for example.
         * 
         */
        namespace FakeDomain
        {
            public static class FakeWork
            {
                public static void NoParameters()
                {
                }
                public static void SingleParameter(string name)
                {
                }
                public static void LotsOfParameters(string name, DateTime Date, int count)
                {
                }
            }
    
            public class StringParameter
            {
                public string Name { get; set; }
            }
    
            public class LotsOfParameters
            {
                public string Name { get; set; }
                public DateTime Date { get; set; }
                public int Count { get; set; }
            }
        }
    
        /*
         * Advantages: 
         *      - no additional types required         
         * Disadvantages
         *      - not strongly typed
         *      - requires explicit casting
         *      - requires "positional" array references 
         *      - no compile time checking for type safety/correct indexing position
         *      - harder to maintin if parameters change
         */
        namespace UsingParams
        {
            public delegate void NoParametersWrapperDelegate();
            public delegate void ParamsWrapperDelegate(params object[] parameters);
    
            public class TimedProcess : IDisposable
            {
                public TimedProcess()
                    : this(0)
                {
                }
    
                public TimedProcess(int interval)
                {
                    if (interval > 0)
                        InitTimer(interval);
                }
    
                public TimedProcess(int interval, NoParametersWrapperDelegate task)
                    : this(interval, p => task(), null) { }
    
                public TimedProcess(int interval, ParamsWrapperDelegate task, params object[] parameters)
                    : this(interval)
                {
                    _task = task;
                    _parameters = parameters;
                }
    
                private Timer timer;
                private ParamsWrapperDelegate _task;
                private object[] _parameters;
    
                public bool InProcess { get; protected set; }
    
                public bool Running
                {
                    get
                    {
                        return timer.Enabled;
                    }
                }
    
                private void InitTimer(int interval)
                {
                    if (timer == null)
                    {
                        timer = new Timer();
                        timer.Elapsed += TimerElapsed;
                    }
                    timer.Interval = interval;
                }
    
                public void InitExecuteProcess()
                {
                    timer.Stop();
                    InProcess = true;
                    RunTask();
                    InProcess = false;
                    timer.Start();
                }
    
                public void RunTask()
                {
                    TimedProcessRunner.RunTask(_task, _parameters);
                }
    
                public void TimerElapsed(object sender, ElapsedEventArgs e)
                {
                    InitExecuteProcess();
                }
    
                public void Start()
                {
                    if (timer != null && timer.Interval > 0)
                        timer.Start();
                }
    
                public void Start(int interval)
                {
                    InitTimer(interval);
                    Start();
                }
    
                public void Stop()
                {
                    timer.Stop();
                }
    
                private bool disposed = false;
    
                public void Dispose(bool disposing)
                {
                    if (disposed || !disposing)
                        return;
    
                    timer.Dispose();
    
                    disposed = true;
                }
    
                public void Dispose()
                {
                    Dispose(true);
                }
    
                ~TimedProcess()
                {
                    Dispose(false);
                }
            }
    
            public static class TimedProcessRunner
            {
                public static void RunTask(ParamsWrapperDelegate task)
                {
                    RunTask(task, null);
                }
    
                public static void RunTask(ParamsWrapperDelegate task, params object[] parameters)
                {
                    task.Invoke(parameters);
                }
            }
        }
    
        /*
         * Advantage of this method: 
         *      - everything is strongly typed         
         *      - compile time and "IDE time" verified
         * Disadvantages:
         *      - requires more custom types 
         */
        namespace UsingGenerics
        {
            public class TimedProcess : TimedProcess<object>
            {
                public TimedProcess()
                    : base() { }
                public TimedProcess(int interval)
                    : base(interval) { }
                public TimedProcess(int interval, NoParametersWrapperDelegate task)
                    : base(interval, task) { }
            }
    
            public class TimedProcess<TParam>
            {
                public TimedProcess()
                    : this(0)
                {
                }
    
                public TimedProcess(int interval)
                {
                    if (interval > 0)
                        InitTimer(interval);
                }
                public TimedProcess(int interval, NoParametersWrapperDelegate task)
                    : this(interval, p => task(), default(TParam)) { }
    
                public TimedProcess(int interval, WrapperDelegate<TParam> task, TParam parameters)
                    : this(interval)
                {
                    _task = task;
                    _parameters = parameters;
                }
    
                private Timer timer;
                private WrapperDelegate<TParam> _task;
                private TParam _parameters;
    
                public bool InProcess { get; protected set; }
    
                public bool Running
                {
                    get
                    {
                        return timer.Enabled;
                    }
                }
    
                private void InitTimer(int interval)
                {
                    if (timer == null)
                    {
                        timer = new Timer();
                        timer.Elapsed += TimerElapsed;
                    }
                    timer.Interval = interval;
                }
    
                public void InitExecuteProcess()
                {
                    timer.Stop();
                    InProcess = true;
                    RunTask();
                    InProcess = false;
                    timer.Start();
                }
    
                public void RunTask()
                {
                    TaskRunner.RunTask(_task, _parameters);
                }
    
                public void TimerElapsed(object sender, ElapsedEventArgs e)
                {
                    InitExecuteProcess();
                }
    
                public void Start()
                {
                    if (timer != null && timer.Interval > 0)
                        timer.Start();
                }
    
                public void Start(int interval)
                {
                    InitTimer(interval);
                    Start();
                }
    
                public void Stop()
                {
                    timer.Stop();
                }
    
                private bool disposed = false;
    
                public void Dispose(bool disposing)
                {
                    if (disposed || !disposing)
                        return;
    
                    timer.Dispose();
    
                    disposed = true;
                }
    
                public void Dispose()
                {
                    Dispose(true);
                }
    
                ~TimedProcess()
                {
                    Dispose(false);
                }
            }
    
            public delegate void NoParametersWrapperDelegate();
            public delegate void WrapperDelegate<TParam>(TParam parameters);
    
            public static class TaskRunner
            {
                public static void RunTask<TParam>(WrapperDelegate<TParam> task)
                {
                    RunTask(task, default(TParam));
                }
    
                public static void RunTask<TParam>(WrapperDelegate<TParam> task, TParam parameters)
                {
                    task.Invoke(parameters);
                }
            }
        }
    }
    
        5
  •  0
  •   scmccart    17 年前

    如果委托的方法参数或属性只是delegate类型,则可以使用它的DynamicInvoke方法调用委托,而不管它的签名是什么。像这样:

    public void CallDelegate(Delegate del) {
      result = del.DynamicInvoke(1, 2, 3, 'A', 'B', 'C');
    }
    

    public void CallDelegate(Func<int, int, char, char> del) {
      result = del(1, 2, 'A', 'B');
    }
    

    就我个人而言,我会创建一个所有流程都必须实现的接口,然后让服务发现实现它的所有对象。通过这种方式,他们可以提供自己的时间需求和调用服务的强类型方法。大概是这样的:

    //The interface
    interface ITimerProcess {
      TimeSpan Period {get;}
      void PerformAction(string someInfo);
    }
    
    //A process
    class SayHelloProcess : ITimerProcess {
    
      public TimeSpan Period { get { return TimeSpan.FromHours(1); } }
    
      public void PerformAction(string someInfo) {
        Console.WriteLine("Hello, {0}!", someInfo);
      }
    }
    

    对于brievity,我将在这里结束,您的服务可以通过查找实现ITimerProcess的类,然后根据每个公开的句点属性为每个类创建计时器来描述整个流程。计时器只需调用PerformAction,并使用您希望传递的任何其他额外数据即可。

    推荐文章