へくれすブログ

UnityとC#中心の備忘録です

【Unity】【UniRx】ストリーム内での例外発生箇所を調べる方法

UniRxの例外出力を分かりやすくするTipsについてです。
.NetFrameWork4.5以降で可能なやり方なのでご注意ください。 f:id:hecres:20180303223010j:plain

UniRxは非常に便利で強力なアセットですが、個人的に1つ不満があります。
ストリームで例外が発生した際にスタックトレースを握りつぶしてしまうことです。

例えば、以下のようなストリームでエラーが発生した場合を考えます。

あまり良い例ではないかもしれませんがリソースの非同期ロード処理です。

対応にあたってまず知りたいのはエラーの発生箇所でしょう。
エラーメッセージで原因を探ることもできますがNullReferenceExceptionなどだと発生個所のほうが重要です。
コンソールのメッセージをダブルクリックし、処理ABCのどれに飛ぶか確認……と行きたいところです。

しかし残念ながら飛び先は処理ABCのどれでもないObserver.csやListObserver.csなどです。

ストリーム内で発生したエラーはActionデリゲートに格納 or OnErrorメソッドに渡され、 throw exception;の形式で再スローされています。

スタックトレースを保持する例外の出し方

try-catchでキャッチした例外の再スローはthrow;とthrow ex;で出力ログが変わります。

throw;と書いた場合は元のスタックトレースを保持したエラーメッセージが出力され、
throw exception;と書いた場合は元のスタックトレースが消えてしまいます。

UniRxがストリーム内でのスタックトレースを消してしまうのは上記の仕様が原因です。

なので該当箇所をちょっと修正してみよう、と言いたいところですが
ここで壁となるのがUniRxはCache句の外で再スローしているという点です。

単にthrow;と書き直すだけではエラーとなってしまいます(Cache句の中で書けと怒られます)。

ExceptionDispatchInfoクラスを使う

解決策としてExceptionDispatchInfoクラスを使用しましょう。
このクラスを使うとCatch句の外でもスタックトレースを保持したまま例外の再スローが行えます。

書き方は以下の通り。

これだけでスタックトレース問題は解決です。

UniRxの該当箇所も同じようにして置き換えて見ましょう。

これでストリーム内のエラーも発生箇所まで瞬時に飛べるようになりました。

対応バージョンについて

冒頭に記述した通り本記事は.NetFrameWork4.5以降でのみ可能なTipsです。
ExceptionDispatchInfoが追加されたのは.NetFrameWork4.5で、以前のバージョンでは肝心要となるクラスが使えません。

本記事を書いている時点でUnityの最新バージョンは2017.3です(ベータは2018)。
2017.3では.NetFrameWork4.6を選択できますが、表記がExperimental (.NET 4.6 Equivalent)となっているはずです。

Experimental = 実験的。つまり使えるけど正式なものではないよ、ということです。
個人ならともかく業務で採用するには少し躊躇ってしまう要素だと思います。

おそらく元々のUniRxがExceptionDispatchInfoクラスを使っていないのはこれが理由で、
今後Unityが正式にC#6対応したならバージョンアップで置き換わるのではないでしょうか。

参考/関連

qiita.com

この記事でのバージョン

Version
Unity 2017.3.0p4
Unity/PlayerSettings/Scripting Runtime Version Experimental (.NET 4.6 Equivalent)
UniRx 5.7.0