我正在尝试等待按下按钮时触发的回调。重要的一点是,我要等待简单的await
中的回调,而无需重新设计代码。换句话说,我要实现以下目标:
internal async Task BatchLogic()
{
ProgressMessage = "Batch Logic Starts";
await OnCallbackFired();
ProgressMessage = "Batch Logic Ends";
}
我的尝试是
internal async Task BatchLogic()
{
ProgressMessage = "Batch Logic Starts";
await Task.Factory.FromAsync(beginMethod, endMethod, state);
ProgressMessage = "Batch Logic Ends";
}
具有以下定义
private object state = null;
private void endMethod(IAsyncResult obj)
{
IsBusy = false;
}
private AsyncCallback callback;
private IAsyncResult beginMethod(AsyncCallback callback, object state)
{
return Task.FromResult(true);
}
当按下按钮时,将执行以下代码:
private async void RunNext()
{
isBusy = true;
await workToDo();
isBusy = false; // the first 3 lines are not relevant
callback = new AsyncCallback(endMethod);
callback.Invoke(Task.FromResult(true));
}
问题是endMethod
是从callback.Invoke
调用的,但是Factory.FromAsync
从未返回,这很可能是因为我不了解如何使用它,也没有找到与我所用的示例相对应的示例我试图实现。
要await
,它必须是Task
(这是一个过分的简化,但这是重要的部分)((C#可以await
任何公开了TaskAwaiter GetAwaiter()
方法的对象,但95%的C#开发人员将消耗Task
/ Task<T>
对象的时间)。。
要创建Task
,请使用TaskCompletionSource
。这样就可以围绕其他概念性“任务”创建Task
表示形式,例如IAsyncResult
样式的API,线程,重叠的IO等。
假设这是WinForms,然后执行此操作:
class MyForm : Form
{
// Note that `TaskCompletionSource` must have a generic-type argument.
// If you don't care about the result then use a dummy null `Object` or 1-byte `Boolean` value.
private TaskCompletionSource<Button> tcs;
private readonly Button button;
public MyForm()
{
this.button = new Button() { Text = "Click me" };
this.Controls.Add( this.button );
this.button.Click += this.OnButtonClicked;
}
private void OnButtonClicked( Object sender, EventArgs e )
{
if( this.tcs == null ) return;
this.tcs.SetResult( (Button)sender );
}
internal async Task BatchLogicAsync()
{
this.tcs = new TaskCompletionSource<Button>();
ProgressMessage = "Batch Logic Starts";
Button clickedButton = await this.tcs.Task;
ProgressMessage = "Batch Logic Ends";
}
}
可接受的答案要简单得多,但是,出于此原因,这是我的初始代码的解决方案,它可以按预期运行。我必须为回调定义一个闭包
AsyncCallback callback;
然后我不得不将callback
从beginMethod
传递给闭包:
private IAsyncResult beginMethod(AsyncCallback callback, object state)
{
this.callback = callback;
return Task.FromResult(true);
}
最后从事件中调用它(即,用于WPF的MVVM中的Command
方法)
private async void RunNext()
{
IsBusy = true;
ProgressMessage = "Wait 10 seconds...";
await workToDo();
ProgressMessage = "Work done!";
IsBusy = false;
if (callback != null)
{
callback.Invoke(Task.FromResult(true));
}
}