ある日、デバッガから「岩崎さん、ハングアップします」とレポートが上がってきた。
レポートに貼ってあるログを見ると…
NullReferenceException: Object reference not set to an instance of an object
HogehogeManager+<>c.<Gethogehoge>b__45_2 (HogeEntity a, HogeEntity b) (at Assets/Scripts/Battle/HogehogeManager.cs:448)
System.Collections.Generic.ArraySortHelper`1[T].PickPivotAndPartition (T[] keys, System.Int32 lo, System.Int32 hi, System.Comparison`1[T] comparer) (at <b89873cb176e44a995a4781c7487d410>:0)
System.Collections.Generic.ArraySortHelper`1[T].IntroSort (T[] keys, System.Int32 lo, System.Int32 hi, System.Int32 depthLimit, System.Comparison`1[T] comparer) (at
「は?」 なんでSortがnull吐いてんのよ?
となって、調べると、落ちているのはなんと、こんなSort。
List<T>.Sort((a, b) => ((a.x - b.x)>0)?1:-1 );
どうして落ちているのかさっぱりわからず、でも試すと、確かに特定条件下でnullを吐く。
しょうがないので、とりあえずノームソートを書いて、それだと落ちないのを確認したのち、ふっと気がついた。
「まさかとは思うけれど、ラムダ式の書き方で落ちる?」
で、ラムダ式を以下に書き換えてみた。
List<T>.Sort((a, b) => ((a.x.CompareTo(b.x) );
なんてこったい、落ちなくなったぞ!?
なんでや!? と、調べていくと、stackoverflowのこのスレッドに当たった。
で、読むと、端的に書けば比較関数に一貫性がないとNull で落ちるという話が書かれている。
でも、 a.x > b.x ? 1:-1
のどこに一貫性がないなんてことがあんだよ…と、考えていて気がついた。
a.x == b.x
のとき、 a.x>b.x
と b.x>a.x
の どっちも-1を返す 、すなわち一貫性がない、だからnull落ちしてたんだ!
と、気がついて、理由が腹落ちしたところに、この話をslackのスレッドで展開してたところ、スーパー出来る同僚が曰く。
てか、List<T>.Sort((a, b) => (a.x - b.x)
だけでOKでは…
正負または0を返せばいいだけで、-1/1に限定する必要はない
ガ━━(;゚Д゚)━( ゚Д)━( ゚)━( )━(゚; )━(Д゚; )━(゚Д゚;)━━ン!!!!!
俺って、バカ。
というわけで、教訓。
- UnityのC#のSortは比較関数が一貫性のない値を返すとnull落ちする。そしてこれはとてもわかりにくい
- だいたいSortを1/0/-1に限定する必要はない
- コメントでこれはmonoの古い目のやつのバグだと思うという話があったのでUnityの、に限定しておきました
- なお、出ているソースはもちろん「そのまま」ではないのはご了承くださいw