尝试使方法等待内部事件处理完成

4

我是C#异步/等待方面的新手,在尝试使用异步方法时遇到了一些问题。 我有一个集合:

private IList<IContactInfo> _contactInfoList

还有一个异步方法:

public async Task<IList<IContactInfo>> SelectContacts()
{
    _contactInfoList = new List<IContactInfo>();
    ContactsSelector selector = new ContactsSelector();
    selector.ShowPicker();

    selector.ContactsSelected += (object sender, ContactsSelectorEventArgs e) =>
    {
        this._contactInfoList = e.Contacts;                
    };

    return _contactInfoList;
}

联系人选择器是一个弹出式用户控件,允许从手机中选择一些联系人,并在点击“确定”按钮后触发ContactsSelected事件。我需要从事件参数e.Contacts中获取所选联系人列表,并在上述提到的SelectContacts()异步方法中返回该列表。这里有一个问题:我的方法在ContactsSelected事件完成之前就已经返回了空列表_contactInfoList。我知道即使使用async / await,这个问题也不会解决,而且在普通方法中也会存在这个问题,但我只需要让该方法等待事件处理结果。

1个回答

4
您需要在这里将事件驱动的异步编程转换为任务驱动的异步编程。使用 TaskCompletionSource 将使此过程相对简单。
public static Task<IList<IContactInfo>> WhenContactsSelected(
    this ContactsSelector selector)
{
    var tcs = new TaskCompletionSource<IList<IContactInfo>>();
    selector.ContactsSelected += (object sender, ContactsSelectorEventArgs e) =>
    {
        tcs.TrySetResult(e.Contacts);
    };
    return tcs.Task;
}

现在我们有了一个返回所需结果的任务的方法,使用它的方法非常简单:

public Task<IList<IContactInfo>> SelectContacts()
{
    ContactsSelector selector = new ContactsSelector();
    selector.ShowPicker();

    return selector.WhenContactsSelected();
}

这里有几点需要注意。首先,我删除了实例字段;这在这里似乎是个坏主意。如果SelectContacts被多次调用,它们之间会发生冲突。逻辑上,如果确实需要存储列表,应该使用本地变量。其次,这里没有使用await,所以该方法不应标记为async。如果您想await调用WhenContactsSelected,则可以随时添加async,但目前我看不出真正需要它的理由。


@Servy,为什么你使用TrySetResult而不是SetResult?在事件处理程序中添加try/catch并在try中调用tcs.SetResult,在catch中调用tcs.SetException是否是个好主意? - avo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接