思てたんとちがう!
C#
のAction
をコールバックのリスナとして利用していたところ「思てたんとちがう!」という挙動があったので、改めて検証しました。
Unity
の.NET 4.x
で実行していますが、C#
ならどのプラットフォームでも同じことが起こると思います。
テストコード
テストコード
// コールバック用のリスナー
public event Action OnSomething;
private void Start()
{
// コールバックをそれぞれ登録
OnSomething += OnSomething1;
OnSomething += OnSomething2;
OnSomething += OnSomething3;
// イベントを実行
OnSomething?.Invoke();
}
private void OnSomething1()
{
Debug.Log("1");
}
private void OnSomething2()
{
throw new NotImplementedException();
}
private void OnSomething3()
{
Debug.Log("3");
}
このコードを実行するとこうなります。
ログ
1
NotImplementedException: The method or operation is not implemented.
本来ならばOnSomething1〜3は独立のため、2でException
が起きても3が実行されてほしいところですが、出力されていません。
こんな感じでtry-catch
してあげると3も実行されます。
try-catch
private void OnSomething2()
{
try
{
throw new NotImplementedException();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
ログ
1
NotImplementedException: The method or operation is not implemented.
3
何が怖いか
「コールバック公開しておくから、誰でも好きにイベント登録しといてくれよな!」というのがわりと一般的な使い方と思いますが、この挙動だと「他のところで登録した誰かがthrow
したら自分のところにイベントが来なくなる(しかもそれを知ることすらできない)」というなかなか恐ろしいことになります。
C#
のAction
は他にもラムダ式でイベント登録すると解除できなかったり、いろいろ「コワ〜」な挙動が多いです。それすらもなかった昔よりはマシになっているだろうと言われればそうなのですが……。
なにはともあれ、良い子はイベント購読側で、イベントを購読しているという状態をインスタンスとして保持・破棄できるReactiveExtension
とかUniRx
使おうね!1 という話でした。
おしまい。
-
とはいえ
Rx
もまだ人類には早すぎる代物感がある。 ↩