알 수없는 수의 매개 변수를 가진 대의원을 받기 위해 클래스를 설계하려면 어떻게해야합니까?
문제
나는 계속해서 줄을 서서 시간이 정해진 프로세스를 실행하는 시간이 지정된 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