传递方法要执行异步

时间:2014-02-04 22:10:17

标签: c# .net winforms asynchronous async-await

我正在创建一个Windows表单应用程序,它对一段数据执行许多类似的操作。所有这些操作几乎相同,所以我想创建一个

的方法
  • 使用户界面为只读
  • 设置“执行操作1 ...”等消息
  • 异步执行操作,因此用户界面保持响应
  • 将消息设置为“Operation1 finished。”
  • 将接口只读设置为false。

我想把这个方法称为

ExecuteOperation([method to execute], "Executing operation1")

我确信这很简单,但我在代理和任务之间丢失了所以请告诉我如何编写一个能够运行所选方法的方法,最好使用多个参数以及如何调用此方法。

注意:

通过禁用界面我的意思是

MainMenu.Enabled = false;
txtData.ReadOnly = true;

3 个答案:

答案 0 :(得分:3)

你可能正在寻找这样的东西(我在这里展示了一个带参数的方法的例子,因为你特别要求这个案例):

    private async Task ExecuteOperation(string operationName, Action action)
    {
        //Disable UI here
        //Set 'Executing' message here
        await Task.Run(action);
        //Set 'Finished' message here
        //Enable UI here
    }

    private async Task CallOperation()
    {
        int x, y, z; //These get set to something here...
        await ExecuteOperation("Operation1", () => OperationWithParams(x, y, z));
    }

如果你有各种操作可以抛出的标准异常,那么你很可能也想在你的ExecuteOperation包装器中添加一些异常处理,这会导致某种类型的UI反馈。

在旁注中,我重新排序了名称和操作,因为这使得传递匿名方法作为操作更加清晰(IMO)。

答案 1 :(得分:1)

您可以将方法传递给方法,可以这么说,但代表必须匹配。如果您的方法具有不同的签名,那么您的方法就是错误的。

Action和Func委托是通用的。有几个匹配几乎任何签名(最多16个参数),但同样,他们必须匹配方法签名。如果您有一个方法有一个参数,另一个有两个或两个有不同类型的参数,则不能将它们传递给同一个代理。

所以,真的取决于你的方法有多相似。如果他们有不同类型的参数,你可能需要“包装”这些调用。

简化示例:

void Main()
{
    List<Action> actions = new List<Action>
    {
        () => Method1("myString"),
        () => Method2("myString2", "myString3")
    };
    foreach(var action in actions) InvokeAction(action);
}

void InvokeAction(Action action)
{
    action();
}

void Method1(string x)
{
    Console.WriteLine(x);
}

void Method2(string y, string z)
{
    Console.WriteLine(y);
    Console.WriteLine(z);
}

另一方面,如果您的方法具有相同的签名,则更简单一点:

void Main()
{
    InvokeAction(Method1, "myString");
    InvokeAction(Method2, "myString2");
}

void InvokeAction(Action<string> action, string param)
{
    action(param);
}

void Method1(string x)
{
    Console.WriteLine(x);
}

void Method2(string y)
{
    Console.WriteLine(y);
}

现在,对于异步运行,它就像使用System.Threading.Task一样简单,如果你在.NET 4.0上。您可以改变我的示例方法:

void InvokeAction(Action<string> action, string param)
{
    Task.Factory.StartNew(() => action(param));
}

答案 2 :(得分:0)

这样的事情(非异步/等待 - 版本):

void Foo<T>(Action<T> action, string message)
{
  MethodWhichMakesMyInterfaceReadOnlyAndSetsMessage(message);
  BackgroundWorker worker = new BackgroundWorker();
  worker.DoWork += (obj, arg) => action.Invoke();
  worker.RunWorkerCompleted += 
       (obj, arg) => 
        {
           MethodWhichMakesMyInterfaceReadWrite();
        };

  worker.RunWorkerAsync();
}

在我意识到你想要async / await和具体任务之前我写了这个 - 然而其他人已经回答了这个问题。您可以为行动创建额外参数所需的任何重载。