繁体   English   中英

C#:如何立即在父事件完成执行之前引发子事件?

[英]C#: How do I raise a child event immediately before the parent event finish executing?

我正在利用第三方的API来控制其硬件。 他们的方法将永远不会返回值,而只会触发将返回已更改的值或更改值时发生异常的事件。 我想这就像UI事件。

假设有一个事件处理程序button_Click() ,除其他功能外,我还使用它来调用函数ThirdPartyAPI.SetSomeStuffConfig(some parameters...) 调用该第三方函数后,我需要立即访问只能通过该函数事件的事件处理程序访问的值或异常。 问题是,由第三方函数引发的事件仅在button_Click()完成后才执行。

有什么解决方案或编码模式可用于解决此问题? button_Click()的事件处理程序完成执行之前,是否可以触发并运行第三方的事件/事件处理程序?

即,我想在button_Click()调用第三方函数后立即访问值和异常。

尝试解决方案

我尝试过的是将button_Click()分为两部分。 调用ThirdPartyAPI.SetSomeStuffConfig(some parameters...)之后出现的部分将放入第三方的事件处理程序中。 然而,这是非常麻烦和限制性的。

第三方API的示例代码

public void Stuff_SetConfig(int id, int mode, string Exception)
{

   //returns the new ID, Mode and exception if unable to set ID or Mode on the hardware
    this.ID = id;
    this.Mode = mode;
}

private void button_Click(object sender, EventArgs e)
{
    ThirdParty.Stuff newStuff = new ThirdParty.Stuff();
    newStuff.StuffSetConfig += new ThirdParty.IThirdPartyStuffEvents_StuffSetConfigEventHandler(Stuff_SetConfig);
    //call some functions...

    newStuff.SetConfig(5, 10); //triggers StuffSetConfigEventHandler
    //call other functions...
    Console.WriteLine("ID is " + this.ID + " and this.Mode is " + this.Mode); //ID and Mode is empty because the Stuff_SetConfig event handler has not yet run.
}

(取决于您要包装的API,类似于EAP)。

基于事件的异步模式 (EAP)中,您调用异步方法并期望在工作完成时引发事件-无论成功还是失败。 这是在.NET中编写异步代码的较旧模式。 当Microsoft引入Task ,尤其是在它们添加asyncawait该语言之后,人们开始对旧模式进行调整以使其看起来像基于任务的异步模式 (TAP)的现代Task返回方法,引起了很多兴趣。

不幸的是,转换EAP并不像适应旧版APM(.NET的原始异步模式)那样简单。 但是,他们确实提供了这样做的指导 他们的示例是带有DownloadFileAsync方法和DownloadFileCompleted事件的较旧的WebClient类。 这是他们的改编:

 public static Task<string> DownloadStringAsync(Uri url) { var tcs = new TaskCompletionSource<string>(); var wc = new WebClient(); wc.DownloadStringCompleted += (s,e) => { if (e.Error != null) tcs.TrySetException(e.Error); else if (e.Cancelled) tcs.TrySetCanceled(); else tcs.TrySetResult(e.Result); }; wc.DownloadStringAsync(url); return tcs.Task; } 

因此,如果您可以使用第三方API进行类似操作,则可以创建包装器,并假装其方法是Task返回的。 一旦知道了这一点,您就可以在button_click处理程序中间awaitTask


用一种方法完成所有操作,我们可以滥用捕获来走私多个结果,但是我建议将TaskCompletionSource内容放入上述单独的方法中,并以不同的方式处理多个结果(C#7元组?):

private async void button_Click(object sender, EventArgs e)
{
    ThirdParty.Stuff newStuff = new ThirdParty.Stuff();

    //call some functions...

    var tcs = new TaskCompletionSource<int>();
    int mode;
    newStuff.SetConfig += (_id, _mode, ex) =>
    {
       if(!string.IsNullOrWhitespace(ex))
       {
          tcs.TrySetException(new Exception(ex));
       }
       else
       {
          mode = _mode;
          tcs.TrySetResult(_id);
       }
    }
    newStuff.SetConfig(5, 10);
    //call other functions...
    var id = await tcs.Task;
    Console.WriteLine("ID is " + id + " and this.Mode is " + mode);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM