通过事件参数实现异步事件处理程序回调

3

当我需要收集事件处理程序中包含异步调用的已更改事件参数数据时,我遇到了问题。如下面的代码所示,MessageReceiver引发事件并从属性“Change”收集数据,然后继续处理。问题在于当事件处理程序是异步的时,它会调用带有“await”的异步方法。在OnMyEvent方法中,调用处理程序后代码立即继续执行,不等待处理程序完成,因此“Change”属性未设置...如何使事情更加“同步”?

public class MyEventArgs:EventArgs
{
    public bool Change { get; set; }
}

public delegate void MyEventHandler(object sender, MyEventArgs e);

public class MessageReceiver
{
    public event MyEventHandler MyEvent;

    public void RaiseEvent()
    {
        OnMyEvent();
    }

    protected void OnMyEvent()
    {
        MyEventArgs e = new MyEventArgs();
        if (MyEvent != null)
            MyEvent(this, e);
        if (e.Change)
            Console.WriteLine("Change occured");
    }
}
public class Form
{
    MessageReceiver mr;
    public Form(MessageReceiver MR)
    {
        mr = MR;
        mr.MyEvent += Mr_MyEvent;
    }

    private async void Mr_MyEvent(object sender, MyEventArgs e)
    {
        string s = await GetString();
        e.Change = true;
    }

    public async Task<string> GetString()
    {
        return await Task.Factory.StartNew(() =>
        {
            return Guid.NewGuid().ToString();
        }
            );
    }
}
public class Program
{
    static void Main(string[] args)
    {
        MessageReceiver mr = new MessageReceiver();
        Form frm = new Form(mr);
        mr.RaiseEvent();
    }
}

因为我正在编写一个处理程序,所以我可以像这样管理从异步方法“GetString”中获取结果: private void Mr_MyEvent(object sender, MyEventArgs e) { string s = GetString().Result; e.Change = true; } 但是如果其他人编写了处理程序呢?该如何在方法“OnMyEvent”中等待事件处理程序完成? - vpetrovic
1个回答

2
在我的博客中,我讨论了各种异步事件处理方法。我推荐使用拖延(Deferral)方法,你可以使用DeferralManagerIDeferralSource类型从我的Nito.AsyncEx.Coordination库实现它:
public class MyEventArgs: EventArgs, IDeferralSource
{
  private readonly DeferralManager _deferralManager;
  public MyEventArgs(DeferralManager deferralManager)
  {
    _deferralManager = deferralManager;
  }
  public bool Change { get; set; }
  public IDisposable GetDeferral() { return _deferralManager.GetDeferral(); }
}

public class MessageReceiver
{
  protected async Task OnMyEventAsync()
  {
    if (MyEvent != null)
    {
      DeferralManager deferralManager = new DeferralManager();
      MyEventArgs e = new MyEventArgs(deferralManager);
      MyEvent(this, e);
      await deferralManager.WaitForDeferralsAsync();
    }
    if (e.Change)
      Console.WriteLine("Change occured");
  }
}

public class Form
{
  private async void Mr_MyEvent(object sender, MyEventArgs e)
  {
    using (e.GetDeferral())
    {
      string s = await GetString();
      e.Change = true;
    }
  }
}

做得很好,我会尝试在我的项目中实现这个方法。因此,我们必须注意“邪恶”的异步事件处理程序,并自己处理这些情况。 - vpetrovic

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