使用委托的延迟方法调用

时间:2013-11-23 08:27:44

标签: c# delegates action func delayed-execution

我的问题与这些问题有点类似:

replay a list of functions and parameters

C# delegate for two methods with different parameters

我的目标是将函数调用及其参数存储在列表中,以便在我的经理类调度的不同线程中调用它们。

  • 调用该函数时,将其自身添加到记住参数和值的函数列表中
  • 当函数结束时,我想返回返回对象(如果有的话)
  • 允许稍后调用函数列表
  • 有不同的方法,签名完全不同 (其中一些有返回值(bool,int,object ..),其中一些没有,并且方法参数的数量不固定)

例如,我想打电话给那样的语句:

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

或者那样:

  

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

或支持界面的其他方式..

如果我想支持延迟函数调用,我的ServerManager.addDoSomething方法(或方法,如果不同的签名)应该是什么样的,我应该使用什么数据结构(WHAT_SHOULD_I_STORE)..我怎样才能找回我的返回值?

我认为,我不能以这种方式使委托通用,我可以用它来存储具有不同签名的方法..

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

我的课程:

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

1 个答案:

答案 0 :(得分:0)

我会使用这种模式:

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

只存储一个简单的Action。您可以对所有这些模式使用此模式,因为提供的参数将由闭包捕获,因此您无需担心单独保存它们。

下面的简单示例演示了我的意思:

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

唯一需要注意的是,如果在添加后将对象作为方法参数更改,但在从列表中调用Action之前,需要注意。如果这样做,Action中使用的值也将被修改(闭包抓取引用)。

举个例子:

下面的代码输出'Test 2'两次,因为在执行Actions之前更改了testString:

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

为防止这种情况发生,您需要确保在第一次将testString添加到操作列表后保持不变,并使用不同的字符串引用进行第二次传递。