背景
ページをまたがるUWPアプリケーションをC#で書いています。ページを跨いで背後で通信処理が生きていて(別タスクでずっと起動している)、その通信処理からデータがあがってきたら現在表示されているページにデータに合わせた処理を施して表示する、ということをやっています。通信処理でデータがあがってきた、というイベントを拾って、各ページに通知を送るにはDelegateを使うのが便利なのですが、問題はページ遷移が複雑化してくるとDelegateにイベントハンドラを登録したかどうかをプログラマが管理するのが手に負えなくなってくることがありまして。それをせめてデバッグ中はちゃんと判定できるようにしておきたい、と思ったわけです。
通信処理側は以下のようなコードで、ReceiveEventHandlerをdelegateしておいて、データが受信されたらInvokeする、という流れですね。
public class Receiver
{
private static readonly Receiver _instance = new Receiver();
public delegate void ReceiveEventHandler(object sender, string type, object e);
public event ReceiveEventHandler OnReceiveData;
・・・
// 通信処理なので、シングルトンにしています
public static Receiver Instance
{
get
{
return _instance;
}
}
private void receiveSomething()
{
// データ受信できた場合(適当)
int number;
OnReceiveData?.Invoke(this, "One Data", number);
}
}
受け取り側はReceiveEventHandlerを += 演算子で登録するだけ、というので単純でやりやすいんですが、重複登録できてしまい、もしそうするとハンドラが2回呼ばれる不幸に見舞われます。
public class SomethingPage : Page
{
Receiver recv = Receiver.Instance;
public void OnNavigatedFrom(NavigationEventArgs e)
{
// ページ遷移でページを抜けたときにイベント登録解除を忘れる(これくらいは初歩的)
}
public void OnNavigatedTo(NavigationEventArgs e)
{
recv.OnReceiveData += OnReceiveData;
}
private void OnReceiveData(object sender, string type, object e)
{
// イベントハンドラに2度入ってきてしまう・・・これくらいなら
// 分かりやすいけど、ページ遷移と不意のページ切断時の
// 処理を並行して書いていたらわけが分からなくなってきた
}
}
GetInvocationList()メソッドで取得可能
登録したハンドラのリストはDelegateを持っている側でGetInvocationList()メソッドで取得することが可能です。たとえばReceiverクラスに以下のようなメソッドを用意してやると、登録前に「もしかして、もう登録されている?」かどうかは判定できます。ということは、登録・解除の手前で以下のようなのを呼んであげれば整合性チェックはできそうですね。
public bool IsEventRegistered(ReceiveEventHandler func)
{
bool isRegistered = false;
Delegate[] list = OnReceiveData?.GetInvocationList();
if (list != null) {
foreach (Delegate d in list)
{
if (d.Method.Name == func.Method.Name &&
d.Target.GetType().FullName == func.Target.GetType().FullName)
{
isRegistered = true;
break;
}
}
}
return isRegistered;
}
Google先生偉大でしたわ・・・
というのを備忘録として載っけようと思って、ふとGoogle検索してみたところ、これぞビンゴ!の処理って検索掛かるんですね(検索語をちゃんと絞れば、ですけれども)。Google先生偉大だわ。