0

My question is a bit similar to these questions:

replay a list of functions and parameters

C# delegate for two methods with different parameters

My goal is to store function calls with their parameters in a list, to call them in a different thread, scheduled by my manager class.

  • When the function is called, add itself to a list of functions remembering the parameters and values
  • When the function ends, I want to get back the return objects (if there is any)
  • Allow the list of functions to be called at a later time
  • There are different methods, with totally different signatures (Some of them has a return value (bool, int, object..), some of them has not, and the number of method parameters is not fixed)

for example I want to call funstions like that:

ServerManager.addDoSomething(ServerManager.SERVICES.Login, serverURL, userName, password); // Login() with bool return type and 3 string parameters
ServerManager.addDoSomething(ServerManager.SERVICES.Query, searchExpr);            // Query() with MyData return type and 1 string parameters
ServerManager.addDoSomething(ServerManager.SERVICES.Modify, searchExpr, newVal);       // Modify() with int return type and 2 string parameters
ServerManager.addDoSomething(ServerManager.SERVICES.Logout);                   // Logout() with void return type and 0 parameters

or like that:

ServerManager.addDoSomething(() => ServerManager.SERVICES.Query (searchExpr));
ServerManager.addDoSomething(() => ServerManager.SERVICES.Modify (searchExpr, newVal)); ServerManager.addDoSomething(() => ServerManager.SERVICES.Logout()); ServerManager.addDoSomething(() => ServerManager.SERVICES.Login(serverURL, userName, password));

or some other way what supports the interface..

How should my ServerManager.addDoSomething method(or methods if different signature) look like, and what data structure should I use (WHAT_SHOULD_I_STORE), if I want to support the delayed function call.. How could I get back my return values?

I think, I can't make delegate generic in that way, that I could use it for storeing for methods with different signatures..

public static void addDoSomething(Delegate delegateParameter, string ...);
or
public static void addDoSomething(Func<...> methodToCall, string ...);
or
public static void addDoSomething(Action methodToCall, string ...);
or
public static void addDoSomething(delegate methodToCall, string ...);

My classes:

public class ServerManager
{
    static List< WHAT_SHOULD_I_STORE > requestFIFO = new List< WHAT_SHOULD_I_STORE >();
    public static IServerConnection SERVICES ;

    static BackgroundWorker worker = new BackgroundWorker();


    public ServerManager()
    {
        SERVICES = new ServerConnection();

        worker.DoWork += (o, ea) =>
        {
            try
            {
                WHAT_SHOULD_I_STORE mr = null;
                Application.Current.Dispatcher.Invoke(new Action(() => mr = popQueueElement() ));

            if (mr != null)
                processRequestFromQueue(mr);  
            }
            catch (Exception)
            {
            }
        };

        worker.RunWorkerCompleted += (o, ea) =>
        {
            worker.RunWorkerAsync();
        };

        if ( ! worker.IsBusy )  worker.RunWorkerAsync();
    }

    private WHAT_SHOULD_I_STORE popQueueElement()
    {
        if (requestFIFO != null && requestFIFO.Count > 0)
        {
            WHAT_SHOULD_I_STORE result = requestFIFO.ElementAt(0);
            requestFIFO.Remove(result);
            return result;
        }
        else
            return null;
    }

    private addDoSomething(...)
    {
    //....
    }

}

public class ServerConnection : IServerConnection 
{
    // Concrete implementations of the IServerManager interface
}

public interface IServerConnection 
{
    bool    Login   (string serverURL, string userName, string password);
    MyData  Query   (string serverURL, searchExpr);
    int     Modify  (string searchExpr, string newVal);
    void    Logout  ();
// ...
}
Community
  • 1
  • 1
elaspog
  • 1,635
  • 3
  • 21
  • 51
  • UPDATE: I am sorry, but I've missed out that the variable named "worker" is an instance of the BackgroundWorker class.. – elaspog Nov 24 '13 at 15:20

1 Answers1

0

I would use this pattern:

ServerManager.addDoSomething(() => ServerManager.SERVICES.Logout());
ServerManager.addDoSomething(() => ServerManager.SERVICES.Modify (searchExpr, newVal)); 

and just store a plain Action. You can use this pattern for all of them because the supplied parameters will be captured by the closure, so you don't need to worry about saving them separately.

The simple example below demonstrates what I mean:

class Program
{
    private static List<Action> actionList = new List<Action>();

    public static void Main(string[] args)
    {
        actionList.Add(() => Console.WriteLine("Test 1!"));
        actionList.Add(() => Console.WriteLine("Test {0}!", 2));

        foreach (var action in actionList)
        {
            action();
        }
    }
}

The only caveat is that you need to be careful if you change an object you are passing as a method argument after you add it, but before you invoke your Action from the list. If you do, the value used in the Action will be modified too (the closure grabs a reference).

As an example:

The code below outputs 'Test 2' twice, because testString is changed before the Actions are executed:

class Program
{
    private static List<Action> actionList = new List<Action>();

    public static void Main(string[] args)
    {
        var testString = "Test 1";
        actionList.Add(() => Console.WriteLine(testString));

        testString = "Test 2";
        actionList.Add(() => Console.WriteLine(testString));

        foreach (var action in actionList)
        {
            action();
        }
    }
}

To prevent this, you need make sure you leave testString unchanged after adding it to the action list the first time, and use a different string reference for passing the second time.

Baldrick
  • 11,712
  • 2
  • 31
  • 35
  • Hi! This is a possible solution, but using this one, how can I get back return values? For example I want to use data what Query, or Modify methods give back, even if they are called in the same thread, or by the BackroundWorker instance. – elaspog Nov 24 '13 at 15:29
  • You could use Func instead of Action - this would allow your code on another thread to grab the returned value from any of the invoked calls. Every call would then return an 'object' - you'd then have to cast these objects as required in order to use them. – Baldrick Nov 25 '13 at 05:08