كيف يمكنني تصميم فصل دراسي لاستقبال مندوب له عدد غير معروف من المعلمات؟

StackOverflow https://stackoverflow.com/questions/1601510

  •  05-07-2019
  •  | 
  •  

سؤال

أجد نفسي مضطرًا دائمًا إلى كتابة خدمات Windows موقوتة التي تقوم باستقصاء قوائم الانتظار الخارجية أو تشغيل عمليات موقوتة.وبالتالي لدي قالب قوي إلى حد ما للقيام بذلك، ولكنني أجد أنه في كل مرة أكتب خدمة للقيام بذلك، أبدأ بنفس القالب ثم أكتب العملية بداخله.

أجد نفسي هذا الصباح أتساءل عما إذا كان بإمكاني بالفعل تحويل هذا القالب إلى إطار عمل يمكنني إدخال عملي فيه.يتكون القالب الأساسي الخاص بي من 122 سطرًا فقط من التعليمات البرمجية.بسبب اختلاف المتطلبات لكل عملية - على سبيل المثال.عدد مختلف من الوسائط، وأنواع وسائط مختلفة، وتبعيات مختلفة (بعضها يعتمد على خدمات الويب، وبعضها يعتمد على قواعد البيانات، وما إلى ذلك) لا أستطيع معرفة كيفية إعداد القالب الأساسي الخاص بي لتلقي عملية مُدخلة.

قلب القالب هو مجرد مؤقت يتوقف عند التهيئة ويبدأ العملية ثم يعيد تشغيل المؤقت بمجرد اكتمال العملية.أقوم بعد ذلك بإضافة طريقة العملية الخاصة بي وأي تبعيات إلى القالب مباشرةً.

هل لدى أي شخص أي أفكار حول كيفية القيام بذلك؟لقد ألقيت نظرة على حقن التبعية وغالبًا ما أستخدمه بالفعل لحقن أشياء مثل الاتصال بمخزن البيانات؟هل هناك طريقة لإدخال مندوب بعدد/نوع غير معروف من المعلمات في الفصل؟هل أنظر إلى هذا بشكل غير صحيح؟

وهذا هو القالب الموجود لدي:

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);
    }
}

TimedProcess.cs

using System;

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

لذلك أتطلع إلى تعديله بحيث تولد خدمة Windows الخاصة بي عملية TimedProcess جديدة وتحقنها بالعملية التي تحتاج إلى التشغيل وبالتالي إزالة رمز TimedProcess من خدمة Windows الخاصة بي بالكامل وجعلها تشير إلى ملف DLL.

يحرر:شكرا لمساعدة الجميع.أدركت أنه إذا قمت بدفع طريقة RunProcess() الخاصة بي خارج مكتبة TimedProcess الخاصة بي وتمريرها الذي - التي كإجراء في المنشئ، فإن هذا يبسط كل شيء بالطريقة التي كنت أتطلع إليها:

[مبسطة للإختصار]

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();
    }
}
هل كانت مفيدة؟

المحلول

أحد الأساليب هنا هو استخدام المتغيرات التي تم التقاطها بحيث يمكن لجميع المفوضين بشكل أساسي يصبح Action أو ربما Func<T> - واترك الباقي للمتصل عبر سحر الأساليب المجهولة والمتغيرات الملتقطة وغيرها - أي.

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

(تنبيه قضائي:احترس من عدم المزامنة/الالتقاط - غالبًا ما يكون مزيجًا سيئًا)

أين DoStuff يقبل Action.ثم عند استدعاء Action تتم إضافة المعلمات تلقائيًا وما إلى ذلك.في بعض أكواد مكتبة RPC، اتبعت هذا الأسلوب بطريقة أخرى، باستخدام Expression - لذلك أقوم بالتعبير عن واجهة الخدمة (كالمعتاد)، ثم لدي طرق مثل:

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

تسمى على سبيل المثال:

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

ثم يقول المتصل ماذا لو كان لدينا تطبيق لهذه الواجهة - إلا أننا لم نفعل ذلك أبدًا؛بدلا من ذلك، نقوم بسحب Expression بعيدًا وانظر إلى الطريقة التي يتم استدعاؤها، والوسائط، وما إلى ذلك - وقم بتمريرها إلى طبقة RPC.إذا كنت مهتما، تمت مناقشته أكثر هنا, أو الكود متاح هنا.

نصائح أخرى

إحدى وسائل الحصول على مفوض يأخذ مجموعة غير معروفة من المعلمات هي تمرير مصفوفة من الكائنات.يمكنك بعد ذلك استخدام طول المصفوفة كعدد من المعلمات، وبما أن أي نوع قابل للتحويل إلى كائن، فيمكنك تمرير أي شيء.

يمكنك استخدام مفوض بدون معلمات لتمثيل "استدعاء الخدمة":

ThreadStart taskToPerform = delegate() { // do your stuff here
   YourService.Call(X, Y, Z);
};
Template.AddProcess(taskToPerform);

للاكتمال، إليك بعض أمثلة التنفيذ.

كلاهما يستخدم منهجية المندوب المغلف التي تمت مناقشتها بالفعل.يستخدم المرء "المعلمات" والآخر يستخدم الأدوية العامة.كلاهما يتجنب مشكلة "المزامنة/الالتقاط".في الواقع، هذا مشابه جدًا لكيفية تنفيذ الأحداث.

نأسف مقدمًا، كل ذلك في كتلة واحدة طويلة من التعليمات البرمجية.لقد قسمتها إلى ثلاث مساحات أسماء فرعية:

  • 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);
            }
        }
    }
}

إذا كانت معلمة الأسلوب أو الخاصية الخاصة بالمفوض من النوع Delegate فقط، فيمكنك استخدام أسلوب DynamicInvoc الخاص به لاستدعاء المفوض، بغض النظر عن توقيعه.مثل ذلك:

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

يجب أن تكون قادرًا حقًا على استخدام مندوب مكتوب قوي، ربما يكون Func أو Action يمكنه أخذ أي معلمات تحتاجها لتمريرها إلى العملية.

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);
  }
}

للإيجاز سأنهي الأمر هنا، يمكن لخدمتك اكتشاف كل العملية من خلال البحث عن الفئات التي تنفذ ITimerProcess، ثم إنشاء مؤقت لكل منها بناءً على خاصية الفترة التي يعرضها كل منها.سيتعين على المؤقت فقط الاتصال بـ PerformanceAction مع أي بيانات إضافية أخرى ترغب في تمريرها.

Are you looking for:- params object[] parameterValues

http://msdn.microsoft.com/en-us/library/w5zay9db%28VS.71%29.aspx

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top