閱讀英文

共用方式為


更新的 .NET Core 事件模式

上一個

上一篇文章討論了最常見的事件模式。 .NET Core 有更寬鬆的模式。 在此版本中,EventHandler<TEventArgs> 定義不再具有條件約束,TEventArgs 必須是衍生自 System.EventArgs的類別。

這會增加您的彈性,並且向後相容。 讓我們從彈性開始。 為System.EventArgs 的實作使用了一種在 System.Object 中定義的方法:MemberwiseClone(),這會建立該對象的淺層複本。 該方法必須使用反映,才能針對衍生自 EventArgs的任何類別實作其功能。 該功能更容易在特定衍生類別中建立。 實際上,衍生自 System.EventArgs 是一種限制您的設計的條件,卻不會帶來任何額外的好處。 事實上,您可以變更 FileFoundArgsSearchDirectoryArgs 的定義,使其不會衍生自 EventArgs。 程序的運作方式完全相同。

如果您再進行一項變更,您也可以將 SearchDirectoryArgs 變更為結構:

C#
internal struct SearchDirectoryArgs
{
    internal string CurrentSearchDirectory { get; }
    internal int TotalDirs { get; }
    internal int CompletedDirs { get; }

    internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this()
    {
        CurrentSearchDirectory = dir;
        TotalDirs = totalDirs;
        CompletedDirs = completedDirs;
    }
}

額外的變更是在輸入初始化所有字段的建構函式之前呼叫無參數建構函式。 如果沒有這個新增內容,C# 的規則會報告屬性在指派之前被存取。

您不應該將 FileFoundArgs 從類別(參考型別)變更為結構(實值型別)。 處理取消的通訊協定需要您以參考方式傳遞事件參數。 如果您進行相同的變更,檔案搜尋類別永遠無法觀察任何事件訂閱者所做的任何變更。 結構的新複本會用於每個訂閱者,而該複本會與檔案搜尋物件所看到的複本不同。

接下來,讓我們來考慮這項變更如何回溯相容。 拿掉條件約束不會影響任何現有的程序代碼。 任何現有的事件自變數類型仍衍生自 System.EventArgs。 回溯兼容性是繼續衍生自 System.EventArgs的主要原因之一。 任何現有的事件訂閱者都是遵循傳統模式之事件的訂閱者。

遵循類似的邏輯,現在建立的任何事件自變數類型在任何現有的程式代碼基底中都不會有任何訂閱者。 未衍生自 System.EventArgs 的新事件類型不會中斷這些程式代碼基底。

具有非同步訂閱者的事件

您有一個最後的模式要學習:如何正確撰寫呼叫非同步程式碼的事件訂閱函式。 挑戰會在 「async」 和 「await」一文中說明。 異步方法可以有 void 傳回類型,但不建議使用。 當您的事件訂閱者程式碼呼叫異步方法時,您只能建立 async void 方法。 事件處理程式簽章需要它。

您需要調和這個相反的指導方針。 無論如何,您必須建立安全的 async void 方法。 您需要實作之模式的基本概念會顯示在下列程式代碼中:

C#
worker.StartWorking += async (sender, eventArgs) =>
{
    try
    {
        await DoWorkAsync();
    }
    catch (Exception e)
    {
        //Some form of logging.
        Console.WriteLine($"Async task failure: {e.ToString()}");
        // Consider gracefully, and quickly exiting.
    }
};

首先,請注意處理程序標示為非同步處理程序。 因為它被指派給事件處理常式委派類型,所以它的傳回類型是 void。 這表示您必須遵循處理程式中顯示的模式,而且不允許將任何例外狀況從異步處理程式的範圍中擲回。 因為它不會傳回任務,所以沒有任務可以進入故障狀態來報告錯誤。 因為方法是異步的,因此方法無法擲回例外狀況。 (呼叫方法會繼續執行,因為它是 async。)實際的執行行為會因不同的環境而有不同的定義。 它可能會終止線程或擁有線程的進程,或讓進程處於不確定狀態。 所有這些潛在結果都是極不受歡迎的。

您應該將 await 表達式包裝在自己的 try 區塊中以進行異步任務。 如果它確實造成錯誤的工作,您可以記錄錯誤。 如果出現應用程式無法復原的錯誤,您可以快速且正常地結束程式。

本文說明 .NET 事件模式的主要更新。 您可能會在您使用的程式庫中看到許多較早期版本的範例。 不過,您也應該瞭解最新的模式。 您可以在 Program.cs 看到範例的已完成程式代碼。

本系列中的下一篇文章可協助您區分在設計中使用 delegatesevents。 它們是類似的概念,該文章可協助您為程式做出最佳決策。

下一個


其他資源

文件