去年我不得不这样做几次,我使用了上面@Judah的代码和他引用的原始示例,但每次我都遇到以下问题:异步调用有效但没有完成。如果我单步执行它,我可以看到它会进入TransferCompletion
方法,但e.UserState == tcs
总是会false
。
事实证明,像 OP 这样的 Web 服务异步方法loginAsync
有两个签名。第二个接受一个userState
参数。解决方案是将TaskCompletionSource<T>
您创建的对象作为此参数传递。这样e.UserState == tcs
将返回true。
在 OP 中,e.UserState == tcs
删除了 以使代码工作是可以理解的 - 我也被诱惑了。但我相信这是为了确保正确的事件完成。
完整的代码是:
public static Task<LoginCompletedEventArgs> RaiseInvoiceAsync(this Client client, string userName, string password)
{
var tcs = CreateSource<LoginCompletedEventArgs>();
LoginCompletedEventHandler handler = null;
handler = (sender, e) => TransferCompletion(tcs, e, () => e, () => client.LoginCompleted -= handler);
client.LoginCompleted += handler;
try
{
client.LoginAsync(userName, password, tcs);
}
catch
{
client.LoginCompleted -= handler;
tcs.TrySetCanceled();
throw;
}
return tcs.Task;
}
或者,我相信也有一个tcs.Task.AsyncState
属性可以提供userState
. 因此,您可以执行以下操作:
if (e.UserState == taskCompletionSource || e.UserState == taskCompletionSource?.Task.AsyncState)
{
if (e.Cancelled) taskCompletionSource.TrySetCanceled();
else if (e.Error != null) taskCompletionSource.TrySetException(e.Error);
else taskCompletionSource.TrySetResult(getResult());
unregisterHandler();
}
这是我最初尝试的,因为它似乎是一种更轻松的方法,我可以传递一个 Guid 而不是完整的 TaskCompletionSource 对象。如果您有兴趣, Stephen Cleary对 AsyncState 有很好的描述。