【问题标题】:delayed method calls using delegates使用委托延迟方法调用
【发布时间】:2013-11-24 23:45:58
【问题描述】:

我的问题和这些问题有点相似:

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

【问题讨论】:

  • 更新:很抱歉,我错过了名为“worker”的变量是 BackgroundWorker 类的一个实例。

标签: c# delegates action func delayed-execution


【解决方案1】:

我会使用这种模式:

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

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

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

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'两次,因为在执行Action之前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 添加到操作列表后保持不变,并使用不同的字符串引用来传递第二次。

【讨论】:

  • 嗨!这是一个可能的解决方案,但是使用这个,我怎样才能取回返回值?例如,我想使用 Query 或 Modify 方法返回的数据,即使它们是在同一个线程中调用的,或者由 BackroundWorker 实例调用。
  • 您可以使用 Func 而不是 Action - 这将允许您在另一个线程上的代码从任何调用的调用中获取返回值。然后,每个调用都会返回一个“对象” - 然后您必须根据需要强制转换这些对象才能使用它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-23
  • 1970-01-01
  • 2011-08-12
  • 2010-10-01
相关资源
最近更新 更多