共用方式為


Windows 執行階段元件中的自訂事件和事件存取子

.NET 對 Windows 執行階段元件的支援透過隱藏通用 Windows 平台 (UWP) 事件模式和 .NET 事件模式之間的差異,可以輕鬆宣告事件元件。 但是,當您在 Windows 執行階段元件中宣告自訂事件存取子時,必須遵循 UWP 中使用的模式。

註冊事件

當您註冊以處理 UWP 中的事件時,新增存取子會傳回令牌。 若要取消註冊,請將此令牌傳遞給移除存取子。 這表示 UWP 事件的新增和移除存取子具有與您習慣的存取子不同的簽章。

幸運的是,Visual Basic 和 C# 編譯器簡化了此過程:當您在 Windows 執行階段元件中宣告具有自訂存取子的事件時,編譯器會自動使用 UWP 模式。 例如,如果您的新增存取子不傳回令牌,您會收到編譯器錯誤。 .NET 提供了兩種類型來支援實作:

  • EventRegistrationToken 結構代表令牌。
  • EventRegistrationTokenTable<T> 類別會建立令牌,並維護令牌和事件處理常式之間的對應。 泛型類型參數是事件引數類型。 第一次為該事件註冊事件處理常式時,您可以為每個事件建立此類別的執行個體。

NumberChanged 事件的以下程式碼顯示了 UWP 事件的基本模式。 在此範例中,事件引數物件 NumberChangedEventArgs 的建構函式會採用代表已變更數值的單一整數參數。

注意:這與編譯器用於在 Windows 執行階段元件中宣告的普通事件的模式相同。

 

private EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
    m_NumberChangedTokenTable = null;

public event EventHandler<NumberChangedEventArgs> NumberChanged
{
    add
    {
        return EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
            .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
            .AddEventHandler(value);
    }
    remove
    {
        EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
            .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
            .RemoveEventHandler(value);
    }
}

internal void OnNumberChanged(int newValue)
{
    EventHandler<NumberChangedEventArgs> temp =
        EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
        .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
        .InvocationList;
    if (temp != null)
    {
        temp(this, new NumberChangedEventArgs(newValue));
    }
}
Private m_NumberChangedTokenTable As  _
    EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs))

Public Custom Event NumberChanged As EventHandler(Of NumberChangedEventArgs)

    AddHandler(ByVal handler As EventHandler(Of NumberChangedEventArgs))
        Return EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            AddEventHandler(handler)
    End AddHandler

    RemoveHandler(ByVal token As EventRegistrationToken)
        EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            RemoveEventHandler(token)
    End RemoveHandler

    RaiseEvent(ByVal sender As Class1, ByVal args As NumberChangedEventArgs)
        Dim temp As EventHandler(Of NumberChangedEventArgs) = _
            EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            InvocationList
        If temp IsNot Nothing Then
            temp(sender, args)
        End If
    End RaiseEvent
End Event

靜態 (在 Visual Basic 中共用) GetOrCreateEventRegistrationTokenTable 方法會延遲建立 EventRegistrationTokenTable<T> 物件的事件執行個體。 將保存令牌資料表執行個體的類別層級欄位傳遞到此方法。 如果該欄位為空,則該方法將建立資料表,在欄位中儲存對該資料表的參考,然後傳回對該資料表的參考。 如果該欄位已包含權杖資料表參考,則該方法僅傳回該參考。

重要:為了確保執行緒安全,保存事件的 EventRegistrationTokenTable<T> 執行個體的欄位必須是類別層級欄位。 如果它是類別層級欄位,則 GetOrCreateEventRegistrationTokenTable 方法可確保當多個執行緒嘗試建立令牌資料表時,所有執行緒都會取得該資料表的相同執行個體。 如果是指定事件,對 GetOrCreateEventRegistrationTokenTable 方法的所有呼叫都必須使用相同的類別層級欄位。

在移除存取子和 RaiseEvent 方法 (在 C# 中為 OnRaiseEvent 方法) 中呼叫 GetOrCreateEventRegistrationTokenTable 方法,可確保在新增任何事件處理常式委派之前呼叫這些方法時不會發生例外狀況。

UWP 事件模式中使用的 EventRegistrationTokenTable<T> 類別的其他成員包括:

  • AddEventHandler 方法會為事件處理常式委派產生令牌,將該委派儲存在資料表中,將其新增至呼叫清單中,然後傳回該令牌。

  • RemoveEventHandler(EventRegistrationToken) 方法會多載從資料表和叫用清單中移除委派。

    注意:AddEventHandler 和 RemoveEventHandler(EventRegistrationToken) 方法會鎖定資料表以協助確保執行緒安全。

  • IncationList 屬性會傳回委派,其中包含目前註冊來處理該事件的所有事件處理常式。 使用此委派引發事件,或使用 Delegate 類別的方法單獨較用處理常式。

    注意:建議您遵循本文先前所提供範例中的模式,並在叫用委派之前將其複製到臨時變數。 這避免了一個執行緒移除最後一個處理常式的競爭條件,從而在另一個執行緒嘗試叫用委派之前,將委派減少為 Null。 委派是不可變的,因此副本仍然有效。

根據需要將您自己的程式碼放入存取子中。 如果執行緒安全性是個問題,則您必須為程式碼提供自己的鎖定方式。

C# 使用者:當您在 UWP 事件模式中撰寫自訂事件存取子時,編譯器不會提供一般的語法捷徑。 如果您在程式碼中使用事件名稱,則會產生錯誤。

Visual Basic 使用者:在 .NET 中,事件只是代表所有已註冊事件處理常式的多點傳送委派。 引發事件只代表叫用委派。 Visual Basic 語法通常會隱藏與委派的互動,並且編譯器在叫用委派之前會複製委派,如執行緒安全性附註中所述。 當您在 Windows 執行階段元件中建立自訂事件時,您必須直接處理委派。 這也代表如果您想單獨叫用處理常式,您可以使用 MulticastDelegate.GetInvocableList 方法取得陣列,其中包含每個事件處理常式的單獨委派。