Wie kann ich eine Klasse entwerfen, einen Delegierten mit einer unbekannten Anzahl von Parametern zu erhalten?

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

  •  05-07-2019
  •  | 
  •  

Frage

ich mich kontinuierlich timed Windows-Dienste schreiben finden aufweist, die Umfrage außerhalb Warteschlangen oder laufen zeitlich Prozesse. Ich habe daher eine ziemlich robuste Vorlage, mit denen dies zu tun, aber ich finde, dass jedes Mal, wenn ich einen Dienst schreiben, dies zu tun, ich mit der gleichen Vorlage beginnen und dann den Prozess hinein schreiben.

An diesem Morgen finde ich mich gefragt, ob ich tatsächlich diese Vorlage in einen Rahmen drehen konnte, dass ich meinen Prozess in injizieren könnte. Meine grundlegende Vorlage ist nur 122 Zeilen Code. Aufgrund unterschiedlicher Anforderungen für jeden Prozess - also unterschiedliche Anzahl von Argumenten, unterschiedliche Argumenttypen und unterschiedliche Abhängigkeiten (einige auf Web Service abhängig sind, einige auf Datenbanken etc.) kann ich nicht herausfinden, wie meine grundlegende Vorlage zur Einrichtung ein erhalten injiziert Prozess.

Das Herz der Vorlage ist nur ein Timer, der stoppt, wenn er initialisiert und startet den Prozess und startet dann den Timer, sobald der Prozess abgeschlossen ist. Ich habe dann meine Prozess Methode hinzufügen und alle Abhängigkeiten direkt in die Vorlage ein.

Hat jemand irgendwelche Ideen, wie dies zu tun? Ich habe bei Dependency Injection geschaut und es oft verwenden bereits für die Injektion von Dingen wie Datenspeicher-Konnektivität? Gibt es eine Möglichkeit, einen Delegierten mit einer unbekannten Anzahl / Art der Parameter in eine Klasse zu injizieren? Bin ich an dieser falsch suchen?

Dies ist die Vorlage habe ich:

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

Also habe ich mich, ihn zu ändern, so dass mein Windows-Dienst eine neue TimedProcess laicht und spritzt es mit dem Prozess, der dadurch ausgeführt werden muss Entfernen des TimedProcess Code von meinem Windows-Dienst vollständig ist und es die DLL verweisen.

Bearbeiten : Danke für die Hilfe aller. Ich erkannte, dass, wenn ich meine RunProcess () -Methode außerhalb meiner TimedProcess Bibliothek schieben und übergeben , die in als Aktion im Konstruktor, dann ist dies alles so vereinfacht ich war auf der Suche nach:

[Vereinfachte der Kürze halber]

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();
    }
}
War es hilfreich?

Lösung

Ein Ansatz wäre hier erfassten Variablen zu verwenden, so dass alle Teilnehmer im Wesentlichen wurde Action oder vielleicht Func<T> - und den Rest an die Anrufer über die Magie der anonymen Methoden verlassen, erfaßt Variablen, usw. - dh

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

(Vorbehalt: Achten Sie auf async / capture - oft eine schlechte Kombination)

wo DoStuff akzeptiert ein Action. Wenn Sie dann den Action aufrufen die Parameter automatisch hinzugefügt werden, usw. In einigen RPC-Bibliothek Code, den ich in die andere Richtung dieser Ansatz gewählt haben, Expression mit - so drücke ich eine Service-Schnittstelle (als normal), und dann Methoden wie:

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

genannt, zum Beispiel:

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

Dann sagt der Anrufer, was zu , wenn wir eine Implementierung dieser Schnittstelle hatte - außer wir eigentlich nie tun; Stattdessen ziehen wir die Expression auseinander und betrachten die Methode aufgerufen wird, die args, usw. - und übergeben diese an die RPC-Schicht. Wenn Sie interessiert sind, ist es mehr hier oder der Code verfügbar hier .

Andere Tipps

Ein Mittel einen Vertreter aufweist, die eine unbekannte Menge von Parametern stattfindet, ist ein Array von Objekten zu übergeben. Anschließend können Sie die Array-Länge als die Anzahl der Parameter verwenden, und da jeder Typ in ein Objekt konvertierbar ist, kann man alles passieren.

Sie können einen Delegaten ohne Parameter verwenden, um den „Service-Aufruf“ darstellen:

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

Für die Vollständigkeit, hier einige Beispielimplementierungen.

Beide verwenden die verpackten Delegierten Methodik bereits diskutiert. Man verwendet „params“ und verwendet man Generika. Beide vermeiden, dass die „async / capture“ -Problem. In der Tat ist dies sehr ähnlich wie die Ereignisse umgesetzt werden.

Es tut uns Leid im Voraus, es ist alles in einem langen Code-Block. Ich habe es aufgeteilt in drei subnamespaces:

  • FakeDomain (hält Mocks zum Beispiel)
  • UsingParams (hält Implementierung, die params-Schlüsselwort verwendet)
  • UsingGenerics (hält Implementierung, die Generika verwendet)

Siehe unten:

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

Wenn Sie Ihre Methodenparameter oder Eigenschaft für die Delegierten nur vom Typ Delegieren Sie können es verwenden, DynamicInvoke Methode ist die Delegierten zu nennen, egal, was es ist Signatur. Wie so:

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

Sie sollten sich wirklich in der Lage einen starken typisierten Delegaten zu verwenden, wahrscheinlich eine Func oder Aktion, die unabhängig von Parametern, die Sie an die proccess passieren müssen nehmen.

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

Ich persönlich würde eine Schnittstelle erstellen, die alle Prozesse umsetzen mussten, dann haben die Service entdecken alle Objekte, die sie implementieren. So können sie ihre Timing-Anforderungen und eine stark typisierte Methode zur Verfügung stellen könnten, um den Dienst zu nennen. So etwas wie folgt aus:

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

Für brievity ich es dort enden würde, könnte Ihr Service descover ganzen Prozess von für Klassen suchen, ITimerProcess implementieren, dann einen Timer für jeden auf dem Zeitraum Eigenschaft jeweils Exposes Basis zu schaffen. Der Timer würde einfach Perform mit jedem anderen zusätzlichen Daten nennen würden Sie weitergeben möchten.

Suchen Sie nach: -  params object [] Parameterwerte

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top