알 수없는 수의 매개 변수를 가진 대의원을 받기 위해 클래스를 설계하려면 어떻게해야합니까?

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를 생성하고 Windows 서비스에서 Timed -Process 코드를 완전히 제거하고 DLL을 참조하는 프로세스를 주입하여 수정하려고합니다.

편집하다: 모두의 도움에 감사드립니다. 타임 프로세스 라이브러리 밖에서 runprocess () 메소드를 밀고 통과한다는 것을 깨달았습니다. 저것 생성자의 동작으로서, 이것은 내가 찾고있는 방식으로 모든 것을 단순화합니다.

간결성을 위해 단순화

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 호출되는 방법, Args 등을보고이를 RPC 층으로 전달하십시오. 관심이 있으시면 더 많이 논의됩니다 여기, 또는 코드를 사용할 수 있습니다 여기.

다른 팁

알 수없는 매개 변수 세트를 취하는 대의원을 갖는 한 가지 수단은 객체 배열을 전달하는 것입니다. 그런 다음 배열 길이를 매개 변수 수로 사용할 수 있으며 모든 유형을 객체로 변환 할 수 있으므로 모든 것을 전달할 수 있습니다.

매개 변수없이 대의원을 사용하여 "서비스 호출"을 나타낼 수 있습니다.

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

완전성을 위해 여기에는 몇 가지 예제 구현이 있습니다.

둘 다 이미 논의 된 래핑 대표 방법론을 사용합니다. 하나는 "매개 변수"를 사용하고 하나는 제네릭을 사용합니다. 둘 다 "비동기 / 캡처"문제를 피합니다. 실제로 이것은 이벤트가 구현되는 방식과 매우 유사합니다.

미리 죄송합니다. 하나의 긴 코드 블록에 있습니다. 나는 그것을 세 개의 서브 니임 스페이스로 나누었습니다.

  • Fakedomain (예 : 모의 보유)
  • 사용 파람 (Params Keyword를 사용하는 구현)
  • 세대 사용 (제네릭을 사용하는 구현)

아래를 참조하십시오 :

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

대의원의 메소드 매개 변수 또는 속성이 유형 대의원 인 경우, 서명이 무엇이든 상관없이 대의원을 호출하기 위해 DynamicInvoke 메소드를 사용할 수 있습니다. 그렇게 :

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

강력한 유형의 대의원, 아마도 Proccess에 전달하는 데 필요한 매개 변수를 취할 수있는 기능이나 조치를 사용할 수 있어야합니다.

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를 구현하는 클래스를 찾아 모든 프로세스를 찾은 다음 각각의 기간 속성에 따라 각각의 타이머를 만들어 내릴 수 있습니다. 타이머는 전달하려는 다른 추가 데이터로 성능을 호출해야합니다.

당신은 다음을 찾고 있습니까 :- Params Object [] parametervalues

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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top