通知の規則
通知の最中に購読者が購読、購読解除、書き戻しをしたときに何が起きるか。
たいていの場合、購読者は値を読んで何かを更新するだけです。このページはその残りを扱います。購読者が自分のコールバックの中から購読者一覧や値そのものを変えたとき、何が起きるか。
購読者は自分のオブザーバブルに書き戻してはいけない
これはコンパイルできません。
score.Subscribe(Clamp);
private void Clamp(int value)
{
if (value > 10)
score.Value = 10; // UDN0012
}
Udonite が断ります。購読者が自分のオブザーバブルへ戻ることはデリゲートを通る循環になり、Udon は間接呼び出しのためにフレームを退避できないので、出てくるプログラムは正しく戻れません。
断られるのが良い結果です。捕まえる形には、戻る一歩がメソッドではなくプロパティのセッターだという静かなものも含まれます。それを走らせるワールドはすぐには壊れません。VRChat が Program execution time exceeded max VM time of 10,0 seconds でビヘイビアを止めるまでフリーズします。十秒間固まったワールドと、役に立つメッセージのない終わり方です。
代入する前に丸めてください。
score.Value = raw > 10 ? 10 : raw;
あるいは別のオブザーバブルに書きます。これは普通にやってよいことです。
通知中の購読解除は次の通知から効く
購読者は自分自身でも他人でも、コールバックの中から購読解除できます。進行中の通知は始めたときの購読者のまま最後まで走り、外れたことは次の通知から効きます。
private void Once(int value)
{
score.Unsubscribe(Once); // 問題ありません。残りはちゃんと呼ばれ、
} // Once は次からは呼ばれません
これは C# のイベントと同じです。 購読者を外すと、歩いている最中のリストを書き換えるのではなく新しいリストが作られます。コールバックの中から購読解除しても安全なのは、同じ理由です。
なので、片付けの最中のビヘイビアがもう一度だけ呼ばれることがあります。 すでに走っている回です。オブジェクトが消えたあとに動いてはいけないハンドラは、購読解除に頼らずハンドラ自身で守ってください。
private void OnScoreChanged(int value)
{
if (closing)
return;
label.text = value.ToString();
}
通知中の購読は次の通知を待つ
通知が進んでいる最中に追加されたハンドラは、その通知では呼ばれません。居合わせた出来事を取り逃がしてはいませんし、加わったばかりの回の中で呼ばれるほうが驚きだからです。
二重に購読しても二回目は何もしない
二重購読になる典型は、Start がもう一度走ったときです。変更ごとに二回呼ばれるのは毎回バグなので、二回目の購読は尊重せずに無視します。
全員に知らせることと、一人に知らせること
Publish() は、何か変わったかどうかに関わらずすべての購読者に通知します。
遅れて来た一人だけを最新にしたいなら、代わりに Subscribe に true を渡します。Publish でもできますが、それは理由もなく他の全員を描き直させますし、数を数えている購読者がいれば数を狂わせます。
score.Subscribe(OnScoreChanged, true); // このハンドラだけ
score.Publish(); // 全員
読むことは常に静か
Count、Contains、IndexOf、ToArray、ContainsKey、TryGetValue、そしてすべてのゲッターは誰にも通知しません。通知するのは変更だけです。
SetSilently と AddSilently は通知せずに変更します。購読者が反応する筋合いのない初期値、たとえば参加時に復元したスコアを入れるためのものです。