[C#.NET][TPL] 利用 TaskCompletionSource 將 EAP 轉換成 TAP

[C#.NET][TPL] 利用 TaskCompletionSource 將 EAP 轉換成 TAP

假使,類別裡面已經開發了 EAP(Event-based Asynchronous Pattern) 非同步模型,比較常用在 UI 的通知,對大都數的開發者會覺得它比 APM 好用許多,雖然大多數 FCL 的 EAP 底層是實作 APM 來的。

EAP 詳細建立方式可參考以下

http://www.dotblogs.com.tw/yc421206/archive/2012/10/25/78910.aspx

有關 TAP 非同步建立步驟可參考以下

http://www.dotblogs.com.tw/yc421206/archive/2013/08/05/113433.aspx

續上篇 http://www.dotblogs.com.tw/yc421206/archive/2012/10/25/78910.aspx,我們也可以透過 TPL 中的 TaskCompletionSource 將 EAP 轉換成 TAP,若你的專案有兩種非同步模型,選一種來轉換就好。


我們來看個簡單的例子,在這裡演示調用 WebClient.DownloadStringAsync 方法(這個方法不是 TAP),然後由 WebClient.DownloadStringCompleted 事件通知 UI 更新,這是大多數人都會用的方法。


private void DownloadString(string address)
{
    WebClient wc = new WebClient();
    wc.DownloadStringCompleted += (sender, e) =>
    {
        if (e.Cancelled)
            this.textBox1.Text = "Cancel";
        else if (e.Error != null)
            this.textBox1.Text = "Error";
        else
            this.textBox1.Text = e.Result;
    };

    wc.DownloadStringAsync(new Uri(address));
}

用戶端調用


private void button_DownloadString_Click(object sender, EventArgs e)
{
    DownloadString("https://www.google.com.tw/");
}

 

這是一個很簡單的例子,一旦若專案裡有成千上萬的通知事件跟 UI 綁在一起,維護起來會相當的痛苦。

 

將 EAP 轉換成 TAP步驟如下:

  1. 命名規則以 Async 為後綴
  2. 返回 Task 或是 Task<TResult>
  3. 調用 TaskCompletionSource 方法

改變 Task 狀態可調用以下三個方法

SetCanceledSetExceptionSetResult 


private Task<string> DownloadStringAsync(string address)
{
    var tcs = new TaskCompletionSource<string>();
    WebClient wc = new WebClient();
    wc.DownloadStringCompleted += (sender, e) =>
    {
        if (e.Cancelled)
            tcs.SetCanceled();
        else if (e.Error != null)
            tcs.SetException(e.Error);
        else
            tcs.SetResult(e.Result);
    };

    wc.DownloadStringAsync(new Uri(address));
    return tcs.Task;
}

 

用戶端調用


private async void button_DownloadStringAsync_Click(object sender, EventArgs e)
{
    var task = DownloadStringAsync("https://www.google.com.tw/");
    await task;
    if (task.IsCanceled)
    {
        this.textBox1.Text = "Cancel";
    }
    else if (task.IsFaulted)
    {
        this.textBox1.Text = "Error";
    }
    else if (task.IsCompleted)
    {
        this.textBox1.Text = task.Result;
    }
}


結論:並非所有的 EAP 都可以透過本篇方法轉換,比如 SerialPort.DataReceived 事件

範例下載:https://dotblogsfile.blob.core.windows.net/user/yc421206/1308/20138711523887.zip

以上文章出自:http://www.dotblogs.com.tw/yc421206/archive/2013/08/07/113628.aspx

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo