Prism の Event Aggregator を学ぶ


[RRISM LIBRARY の Documentation から Event Aggregator](https://prismlibrary.com/docs/event-aggregator.html) の箇所を学んでみます。

そのまんまの翻訳ですが、メモとして。
日本語がへんなところは英語に戻ってそれなりに理解。。。

The Prism Library provides an event mechanism that enables communications between loosely coupled components in the application. This mechanism, based on the event aggregator service, allows publishers and subscribers to communicate through events and still do not have a direct reference to each other.

The EventAggregator provides multicast publish/subscribe functionality. This means there can be multiple publishers that raise the same event and there can be multiple subscribers listening to the same event. Consider using the EventAggregator to publish an event across modules and when sending a message between business logic code, such as controllers and presenters.

Events created with the Prism Library are typed events. This means you can take advantage of compile-time type checking to detect errors before you run the application. In the Prism Library, the EventAggregator allows subscribers or publishers to locate a specific EventBase. The event aggregator also allows for multiple publishers and multiple subscribers, as shown in the following illustration.
(Google翻訳)
Prism Libraryは、アプリケーション内の疎結合コンポーネント間の通信を可能にするイベントメカニズムを提供します。イベントアグリゲーターサービスに基づくこのメカニズムにより、パブリッシャーとサブスクライバーはイベントを介して通信できますが、相互に直接参照することはできません。

EventAggregatorは、マルチキャストパブリッシュ/サブスクライブ機能を提供します。つまり、同じイベントを発生させる複数のパブリッシャーが存在し、同じイベントをリッスンする複数のサブスクライバーが存在する可能性があります。 EventAggregatorを使用してモジュール間でイベントを発行すること、およびコントローラーやプレゼンターなどのビジネスロジックコード間でメッセージを送信することを検討してください。

Prism Libraryで作成されたイベントは型付きイベントです。これは、アプリケーションを実行する前に、コンパイル時の型チェックを利用してエラーを検出できることを意味します。 Prism Libraryでは、EventAggregatorによってサブスクライバーまたはパブリッシャーが特定のEventBaseを見つけることができます。イベントアグリゲーターでは、次の図に示すように、複数のパブリッシャーとサブスクライバーも使用できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/645ad8a2-440d-6da1-ecee-f12b4c0cbe85.png)

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/d0142d9d-2c60-53b6-e210-487d321c3614.png)](https://www.youtube.com/watch?v=xTP9_hN_3xA&feature=emb_logo)

###IEventAggregator
The EventAggregator class is offered as a service in the container and can be retrieved through the IEventAggregatorinterface. The event aggregator is responsible for locating or building events and for keeping a collection of the events in the system.
(Google翻訳)
EventAggregatorクラスはコンテナー内のサービスとして提供され、IEventAggregatorインターフェースを介して取得できます。 イベントアグリゲーターは、イベントの検索または構築、およびシステム内のイベントのコレクションの保持を担当します。

```
public interface IEventAggregator
{
    TEventType GetEvent<TEventType>() where TEventType : EventBase;
}
```
The EventAggregator constructs the event on its first access if it has not already been constructed. This relieves the publisher or subscriber from needing to determine whether the event is available.
(Google翻訳)
EventAggregatorは、まだ構築されていない場合、最初のアクセスでイベントを構築します。 これにより、パブリッシャーまたはサブスクライバーは、イベントが利用可能かどうかを判断する必要がなくなります。
###PubSubEvent
The real work of connecting publishers and subscribers is done by the PubSubEvent class. This is the only implementation of the EventBase class that is included in the Prism Library. This class maintains the list of subscribers and handles event dispatching to the subscribers.

The PubSubEvent class is a generic class that requires the payload type to be defined as the generic type. This helps enforce, at compile time, that publishers and subscribers provide the correct methods for successful event connection. The following code shows a partial definition of the PubSubEvent class.
(Google翻訳)
パブリッシャーとサブスクライバーを接続する実際の作業は、PubSubEventクラスによって行われます。 これは、Prism Libraryに含まれているEventBaseクラスの唯一の実装です。 このクラスは、サブスクライバーのリストを維持し、サブスクライバーへのイベントディスパッチを処理します。

PubSubEventクラスは、ペイロードタイプをジェネリックタイプとして定義する必要があるジェネリッククラスです。 これにより、パブリッシャーとサブスクライバーがイベント接続を成功させるための適切なメソッドを提供するように、コンパイル時に実行できます。 次のコードは、PubSubEventクラスの部分的な定義を示しています。

NOTE
PubSubEvent can be found in the Prism.Events namespace which is located in the Prism.Core NuGet package.
(Google翻訳)
注意
PubSubEventは、Prism.Core NuGetパッケージにあるPrism.Events名前空間にあります。
###Creating an Event
The PubSubEvent<TPayload> is intended to be the base class for an application's or module's specific events. TPayLoad is the type of the event's payload. The payload is the argument that will be passed to subscribers when the event is published.

For example, the following code shows the TickerSymbolSelectedEvent. The payload is a string containing the company symbol. Notice how the implementation for this class is empty.
(Google翻訳)
PubSubEvent <TPayload>は、アプリケーションまたはモジュールの特定のイベントの基本クラスになることを目的としています。 TPayLoadは、イベントのペイロードのタイプです。 ペイロードは、イベントが発行されたときにサブスクライバーに渡される引数です。

たとえば、次のコードはTickerSymbolSelectedEventを示しています。 ペイロードは、会社のシンボルを含む文字列です。 このクラスの実装が空であることに注意してください。

```
public class TickerSymbolSelectedEvent : PubSubEvent<string>{}
```
NOTE
In a composite application, the events are frequently shared between multiple modules, so they are defined in a common place. It is common practice to define these events in a shared assembly such as a "Core" or "Infrastructure" project.
(Google翻訳)
注意
複合アプリケーションでは、イベントは複数のモジュール間で頻繁に共有されるため、共通の場所で定義されます。 「コア」プロジェクトや「インフラストラクチャ」プロジェクトなどの共有アセンブリでこれらのイベントを定義することは一般的な方法です。
###Publishing an Event
Publishers raise an event by retrieving the event from the EventAggregator and calling the Publish method. To access the EventAggregator, you can use dependency injection by adding a parameter of type IEventAggregator to the class constructor.
(Google翻訳)
パブリッシャーは、EventAggregatorからイベントを取得し、Publishメソッドを呼び出すことにより、イベントを発生させます。 EventAggregatorにアクセスするには、クラスコンストラクターにIEventAggregator型のパラメーターを追加することで、依存性注入を使用できます。

```
public class MainPageViewModel
{
    IEventAggregator _eventAggregator;
    public MainPageViewModel(IEventAggregator ea)
    {
        _eventAggregator = ea;
    }
}
```
The following code demonstrates publishing the TickerSymbolSelectedEvent.
(Google翻訳)
以下のコードは、TickerSymbolSelectedEventの公開を示しています。

```
_eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish("STOCK0");
```
###Subscribing to Events
Subscribers can enlist with an event using one of the Subscribe method overloads available on the PubSubEvent class.
(Google翻訳)
サブスクライバーは、PubSubEventクラスで利用可能なSubscribeメソッドオーバーロードの1つを使用してイベントに参加できます。

```
public class MainPageViewModel
{
    public MainPageViewModel(IEventAggregator ea)
    {
        ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews);
    }

    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}
```
There are several ways to subscribe to PubSubEvents. Use the following criteria to help determine which option best suits your needs:

・If you need to be able to update UI elements when an event is received, subscribe to receive the event on the UI thread.
・If you need to filter an event, provide a filter delegate when subscribing.
・If you have performance concerns with events, consider using strongly referenced delegates when subscribing and then manually unsubscribe from the PubSubEvent.
・If none of the preceding is applicable, use a default subscription.

The following sections describe these options.
(Google翻訳)
PubSubEventsをサブスクライブする方法はいくつかあります。 次の基準を使用して、ニーズに最適なオプションを決定してください。

・イベント受信時にUI要素を更新できるようにする必要がある場合は、UIスレッドでイベントを受信するようサブスクライブしてください。
・イベントをフィルタリングする必要がある場合は、サブスクライブ時にフィルターデリゲートを提供してください。
・イベントに関するパフォーマンスの問題がある場合は、サブスクライブするときに強く参照されるデリゲートを使用することを検討してから、PubSubEventから手動でサブスクライブを解除してください。
・上記に該当しない場合は、デフォルトのサブスクリプションをご利用ください。

次のセクションでは、これらのオプションについて説明します。
####Subscribing on the UI Thread
Frequently, subscribers will need to update UI elements in response to events. In WPF, only a UI thread can update UI elements.

By default, the subscriber receives the event on the publisher's thread. If the publisher sends the event from the UI thread, the subscriber can update the UI. However, if the publisher's thread is a background thread, the subscriber may be unable to directly update UI elements. In this case, the subscriber would need to schedule the updates on the UI thread using the Dispatcher class.

The PubSubEvent provided with the Prism Library can assist by allowing the subscriber to automatically receive the event on the UI thread. The subscriber indicates this during subscription, as shown in the following code example.
(Google翻訳)
多くの場合、サブスクライバーはイベントに応答してUI要素を更新する必要があります。 WPFでは、UIスレッドのみがUI要素を更新できます。

デフォルトでは、サブスクライバーはパブリッシャーのスレッドでイベントを受け取ります。 パブリッシャーがUIスレッドからイベントを送信する場合、サブスクライバーはUIを更新できます。 ただし、パブリッシャーのスレッドがバックグラウンドスレッドの場合、サブスクライバーはUI要素を直接更新できない場合があります。 この場合、サブスクライバーはDispatcherクラスを使用してUIスレッドで更新をスケジュールする必要があります。

Prism Libraryで提供されるPubSubEventは、サブスクライバーがUIスレッドでイベントを自動的に受信できるようにすることで支援できます。 次のコード例に示すように、サブスクライバーはサブスクリプション中にこれを示します。

```
public class MainPageViewModel
{
    public MainPageViewModel(IEventAggregator ea)
    {
        ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews, ThreadOption.UIThread);
    }

    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}
```
The following options are available for ThreadOption:

・PublisherThread: Use this setting to receive the event on the publishers' thread. This is the default setting.
・BackgroundThread: Use this setting to asynchronously receive the event on a .NET Framework thread-pool thread.
・UIThread: Use this setting to receive the event on the UI thread.
(Google翻訳)
ThreadOptionには次のオプションがあります。

・PublisherThread：この設定を使用して、発行者のスレッドでイベントを受信します。 これがデフォルトの設定です。
・BackgroundThread：.NET Frameworkのスレッドプールスレッドで非同期にイベントを受信するには、この設定を使用します。
・UIThread：この設定を使用して、UIスレッドでイベントを受信します。

NOTE
In order for PubSubEvent to publish to subscribers on the UI thread, the EventAggregator must initially be constructed on the UI thread.
(Google翻訳)
注意
PubSubEventをUIスレッドのサブスクライバーに公開するには、最初にEventAggregatorをUIスレッドで構築する必要があります。

####Subscription Filtering
Subscribers may not need to handle every instance of a published event. In these cases, the subscriber can use the filter parameter. The filter parameter is of type System.Predicate<TPayLoad> and is a delegate that gets executed when the event is published to determine if the payload of the published event matches a set of criteria required to have the subscriber callback invoked. If the payload does not meet the specified criteria, the subscriber callback is not executed.

Frequently, this filter is supplied as a lambda expression, as shown in the following code example.
(Google翻訳)
サブスクライバーは、発行されたイベントのすべてのインスタンスを処理する必要がない場合があります。 このような場合、サブスクライバーはフィルターパラメーターを使用できます。 フィルターパラメーターはSystem.Predicate <TPayLoad>型で、イベントが発行されたときに実行され、発行されたイベントのペイロードがサブスクライバーコールバックを呼び出すために必要な一連の基準に一致するかどうかを判断するデリゲートです。 ペイロードが指定された基準を満たさない場合、サブスクライバーコールバックは実行されません。

次のコード例に示すように、このフィルターはラムダ式として提供されることがよくあります。

```
public class MainPageViewModel
{
    public MainPageViewModel(IEventAggregator ea)
    {
        TickerSymbolSelectedEvent tickerEvent = ea.GetEvent<TickerSymbolSelectedEvent>();
        tickerEvent.Subscribe(ShowNews, ThreadOption.UIThread, false, 
                              companySymbol => companySymbol == "STOCK0");
    }

    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}
```
NOTE
The Subscribe method returns a subscription token of type Prism.Events.SubscriptionToken that can be used to remove a subscription to the event later. This token is particularly useful when you are using anonymous delegates or lambda expressions as the callback delegate or when you are subscribing the same event handler with different filters.
(Google翻訳)
注意
Subscribeメソッドは、Prism.Events.SubscriptionTokenタイプのサブスクリプショントークンを返します。これを使用して、後でイベントへのサブスクリプションを削除できます。 このトークンは、匿名デリゲートまたはラムダ式をコールバックデリゲートとして使用する場合、または同じイベントハンドラーを異なるフィルターでサブスクライブする場合に特に役立ちます。

NOTE
It is not recommended to modify the payload object from within a callback delegate because several threads could be accessing the payload object simultaneously. You could have the payload be immutable to avoid concurrency errors.
(Google翻訳)
注意
複数のスレッドがペイロードオブジェクトに同時にアクセスしている可能性があるため、コールバックデリゲート内からペイロードオブジェクトを変更することはお勧めしません。 同時実行エラーを回避するために、ペイロードを不変にすることができます。
####Subscribing Using Strong References
If you are raising multiple events in a short period of time and have noticed performance concerns with them, you may need to subscribe with strong delegate references. If you do that, you will then need to manually unsubscribe from the event when disposing the subscriber.

By default, PubSubEvent maintains a weak delegate reference to the subscriber's handler and filter on subscription. This means the reference that PubSubEvent holds on to will not prevent garbage collection of the subscriber. Using a weak delegate reference relieves the subscriber from the need to unsubscribe and allows for proper garbage collection.

However, maintaining this weak delegate reference is slower than a corresponding strong reference. For most applications, this performance will not be noticeable, but if your application publishes a large number of events in a short period of time, you may need to use strong references with PubSubEvent. If you do use strong delegate references, your subscriber should unsubscribe to enable proper garbage collection of your subscribing object when it is no longer used.

To subscribe with a strong reference, use the keepSubscriberReferenceAlive parameter on the Subscribe method, as shown in the following code example.
(Google翻訳)
短期間に複数のイベントを発生させ、パフォーマンスの問題に気付いた場合は、強力なデリゲートリファレンスをサブスクライブする必要がある場合があります。その場合は、サブスクライバーを破棄するときに、イベントから手動でサブスクライブを解除する必要があります。

デフォルトでは、PubSubEventはサブスクライバーのハンドラーへの弱いデリゲート参照を維持し、サブスクリプションをフィルターします。つまり、PubSubEventが保持する参照は、サブスクライバーのガベージコレクションを妨げません。弱いデリゲート参照を使用すると、サブスクライバーがサブスクライブを解除する必要がなくなり、適切なガベージコレクションが可能になります。

ただし、この弱いデリゲート参照の維持は、対応する強い参照よりも遅くなります。ほとんどのアプリケーションでは、このパフォーマンスは目立ちませんが、アプリケーションが短期間に大量のイベントを発行する場合は、PubSubEventで強力な参照を使用する必要がある場合があります。強いデリゲート参照を使用する場合、サブスクライバーはサブスクライブを解除して、サブスクライブオブジェクトが使用されなくなったときに適切なガベージコレクションを有効にする必要があります。

強参照でサブスクライブするには、次のコード例に示すように、SubscribeメソッドでkeepSubscriberReferenceAliveパラメータを使用します。

```
public class MainPageViewModel
{
    public MainPageViewModel(IEventAggregator ea)
    {
        bool keepSubscriberReferenceAlive = true;
        TickerSymbolSelectedEvent tickerEvent = ea.GetEvent<TickerSymbolSelectedEvent>();
        tickerEvent.Subscribe(ShowNews, ThreadOption.UIThread, keepSubscriberReferenceAlive, 
                              companySymbol => companySymbol == "STOCK0");
    }

    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}
```
The keepSubscriberReferenceAlive parameter is of type bool:

・When set to true, the event instance keeps a strong reference to the subscriber instance, thereby not allowing it to get garbage collected. For information about how to unsubscribe, see the section Unsubscribing from an Event later in this topic.
・When set to false (the default value when this parameter omitted), the event maintains a weak reference to the subscriber instance, thereby allowing the garbage collector to dispose the subscriber instance when there are no other references to it. When the subscriber instance gets collected, the event is automatically unsubscribed.
(Google翻訳)
keepSubscriberReferenceAliveパラメーターのタイプはboolです。

・trueに設定すると、イベントインスタンスはサブスクライバーインスタンスへの強い参照を維持するため、ガベージコレクションを実行できません。 サブスクライブを解除する方法については、このトピックの後半の「イベントからのサブスクライブ解除」セクションを参照してください。
・false（このパラメーター省略時のデフォルト値）に設定すると、イベントはサブスクライバーインスタンスへの弱い参照を維持するため、ガベージコレクターは他に参照がない場合にサブスクライバーインスタンスを破棄できます。 サブスクライバーインスタンスが収集されると、イベントは自動的にサブスクライブ解除されます。

###Unsubscribing from an Event
If your subscriber no longer wants to receive events, you can unsubscribe by using your subscriber's handler or you can unsubscribe by using a subscription token.

The following code example shows how to directly unsubscribe to the handler.
(Google翻訳)
サブスクライバーがイベントを受信する必要がなくなった場合は、サブスクライバーのハンドラーを使用してサブスクライブを解除するか、サブスクリプショントークンを使用してサブスクライブを解除できます。

次のコード例は、ハンドラーを直接サブスクライブ解除する方法を示しています。

```
public class MainPageViewModel
{
    TickerSymbolSelectedEvent _event;
    public MainPageViewModel(IEventAggregator ea)
    {
        _event = ea.GetEvent<TickerSymbolSelectedEvent>();
        _event.Subscribe(ShowNews);
    }

    void Unsubscribe()
    {
        _event.Unsubscribe(ShowNews);
    }

    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}
```
The following code example shows how to unsubscribe with a subscription token. The token is supplied as a return value from the Subscribe method.
(Google翻訳)
次のコード例は、サブスクリプショントークンを使用してサブスクライブを解除する方法を示しています。 トークンは、Subscribeメソッドからの戻り値として提供されます。

```
public class MainPageViewModel
{
    TickerSymbolSelectedEvent _event;
    SubscriptionToken _token;
    public MainPageViewModel(IEventAggregator ea)
    {
        _event = ea.GetEvent<TickerSymbolSelectedEvent>();
        _token = _event.Subscribe(ShowNews);
    }

    void Unsubscribe()
    {
        _event.Unsubscribe(_token);
    }

    void ShowNews(string companySymbol)
    {
        //implement logic
    }
}
```