【问题标题】:Async wait for event after sending argument [duplicate]发送参数后异步等待事件[重复]
【发布时间】:2014-08-12 17:42:15
【问题描述】:

我有以下代码:

static void Main(string[] args)
{
    moHost = new Host(
        APIServerType.Simulator,
        "T4Example",
        "112A04B0-5AAF-42F4-994E-FA7CB959C60B",
        "CTS",
        "myUser",
        "myPass");  //automated login

    moHost = Host.Login(
        APIServerType.Simulator,
        "T4Example",
        "112A04B0-5AAF-42F4-994E-FA7CB959C60B"); //manual login window
    moAccounts = moHost.Accounts;

    System.Console.WriteLine(moAccounts.ToString());
}

我知道第一次自动登录是发送的异步请求,我需要“等待”。

并且登录成功会触发:

public event Host.LoginSuccessEventHandler LoginSuccess

虽然会触发登录失败:

public event Host.LoginFailureEventHandler LoginFailure

一般通知将由以下触发:

public event Host.NotificationEventHandler Notification

我该如何处理这个问题并正确等待我的登录成功或失败?

第一次自动登录尝试未显示我已连接,而第二次手动登录窗口确实成功(因此我认为我的凭据很好,现在只是代码没有正确“等待”)。

【问题讨论】:

标签: c# .net asynchronous task-parallel-library async-await


【解决方案1】:

我会使用TaskCompletionSource<T> 来包装可以从Host.Login 触发的事件,并将其用作Host 类型周围的扩展方法:

public static class HostExtensions
{
    public static Task<bool> LoginAsync(this Host host, APIServerType serverType, string name, string id)
    {
        var tcs = new TaskCompletionSource<bool>();
        try
        {
            host.LoginSuccess += (object obj, LoginSuccessEventHandler e) =>
            {
                tcs.SetResult(true);
            }
            
            host.LoginFailure += (object obj, LoginFailureEventHandler e) =>
            {
                tcs.SetResult(false);
            }
        }
        catch (Exception e)
        {
            tcs.SetException(e);
        }
        
        host.Login(serverType, name, id);
        return tcs.Task;
    }
}

如果通知消息很重要,您可以创建一个LoginDetails 类型并将其用作TaskCompletionSource 的返回类型:

public class LoginDetails
{
    public bool IsSuccess { get; set; }
    public string Notification { get; set; }
}

并像这样使用它:

public static Task<LoginDetails> LoginAsync(this Host host, APIServerType serverType, string name, string id)
{
    var tcs = new TaskCompletionSource<LoginDetails>();
    var loginDetails = new LoginDetails();
    try
    {
        host.LoginSuccess += (object obj, LoginSuccessEventHandler e) =>
        {
            loginDetails.IsSuccess = true;
            tcs.SetResult(loginDetails);
        }
        
        host.LoginFailure += (object obj, LoginFailureEventHandler e) =>
        {
            loginDetails.IsSuccess = false;
            tcs.SetResult(loginDetails);
        }
        
        host.Notification += (object obj, NotificationEventHandler e) =>
        {
            loginDetails.Notificaiton = e.Notification // I assume it would look something like this
            tcs.SetResult(loginDetails);
        }
    }
    catch (Exception e)
    {
        tcs.SetException(e);
    }
    
    host.Login(serverType, name, id);
    return tcs.Task;
}

现在您可以在LoginAsync 上使用await

var login = await host.LoginAsync(APIServerType.Simulator, "T4Example", "112A04B0-5AAF-42F4-994E-FA7CB959C60B");

附带说明,如果您在Console 应用程序中使用它,您将不得不使用Result,它将阻塞 而不是await(将控制权交还给调用者)在Main 内部,这有点可惜,因为它会丢失异步调用的全部要点:

var login = host.LoginAsync(APIServerType.Simulator, "T4Example", "112A04B0-5AAF-42F4-994E-FA7CB959C60B").Result;

【讨论】:

  • 你好,这段代码有一些错误。 return tcs.Task; 例如告诉我a return keyword must not be followed by an object expression。有什么想法吗?
  • 修复了我的代码。这些方法应分别返回Task&lt;bool&gt;Task&lt;LoginDetails&gt;
  • 谢谢你,我会在这个周末看起来很抱歉还没有接受。非常感谢您花时间调整代码。
  • 你不需要为返回添加等待吗? (返回等待 tcs.Task;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 2020-01-27
  • 2019-07-07
  • 1970-01-01
相关资源
最近更新 更多