【LINQのエラー】LINQ for iOSを使ってみた【避けられる？】

この投稿は、「[Unity アセット真夏のアドベントカレンダー 2014 Summer！](http://unityassetjp.doorkeeper.jp/events/12843)」の8/28(木)の記事です。

前日8/27(水)の記事は、[おこし](https://twitter.com/oxosi)さんの[Unityミュージック](http://oxosi.hatenablog.com/entry/2014/08/21/211456)です。
翌日8/29(金)の記事は、nanoさんの[UNITY コースター](http://qiita.com/nano06126728/items/1da409f50d66e03931eb)です。

# はじめに
　この投稿では、[LINQ for iOS](https://www.assetstore.unity3d.com/jp/#!/content/18131)を紹介します。

　「[Unity アセット真夏のアドベントカレンダー 2014 Summer！](http://unityassetjp.doorkeeper.jp/events/12843)」では、Assetの作者様がご自身の作られたAssetを紹介したり、とても便利だけれどもまだ有名でないAssetを紹介したり、有名なAssetの有用な使い方を紹介する素敵な記事が多いのではないでしょうか。今回はちょっと雰囲気を変えてお送りします。

　最初に言っておきます。現時点で、「LINQ for iOS (バージョン0.2、2014/08/02時点)」は微妙でした。エラーが起きなくなったメソッドもあったのですが、いくつかのメソッドではエラーがおきました。今後のバージョンアップで改善はされるかもしれません。


# LINQ for iOSの前に、そもそもLINQとは？

　LINQ、最高ですよね！みなさんLINQ使いましょう！

　LINQ(ここではLINQ to Objects)をとてもざっくり説明すると、「情報の集まり (シーケンス) を上手に扱うことができる構文、ライブラリ、メソッド」です。

　LINQのメソッドを使って、コードが非常に短く簡潔に読みやすくなる例を一つだけ紹介します。多分みなさんがRPGなどを作る際に使えるコードですよ。

　こんなPlayerクラスがあって、

```csharp:Playerクラス
public class Player
{
     public string Name { get; set; }
     public int Hp { get; set; }
     public int Level { get; set; }
}
```

　こんなList\<Player\>型のpartyがあります。

```csharp:party
List<Player> party = GetParty ();
```

　partyの要素の中で、一つでもHpが0以下の(死んでいる)ものがあるかどうか調べるコードを書いてみます。教会で復活させるキャラクターがいるかどうか調べるときに使いそうですね。

```csharp:party中に一つでも死んでいる要素があるか調べる
bool isAnyoneDead = false;
foreach (Player player in party) {
	if (player.Hp <= 0) {
		isAnyoneDead = true;
		break;
	}
}
```

　このコードをLINQを使って書きかえます。

```csharp:LINQを使って、party中に一つでも死んでいる要素があるか調べる
bool isAnyoneDead = party.Any (player => player.Hp <= 0);
```

　7行のコードが1行になってしまいました！短く、簡潔ですね！LINQに慣れていないうちは、奇妙に見えてしまうかもしれません。ですがLINQを理解すると、LINQで書いたコードは短いだけでなく、何をしたいのかが非常に読みやすいのです。

　LINQ、最高ですよね！みなさんLINQ使いましょう！

# LINQは便利なんだが、Unity+iOSだと
　さて、そんな便利なLINQですがちょっと困ったことがあります。

　Unityは様々なプラットフォーム向けにゲームを開発することが可能ですね。ゲーム専用機、WebPlayer、各種モバイル端末など本当に多くのプラットフォームに向けて開発することが可能ですね。そんな中で、iOS向けのゲームを開発している方も多いのではないでしょうか。

　実はUnityで作ったゲームをiOSで動かす場合、AOT(Ahead-Of-Time)コンパイル関連にまつわるエラーが実行時に発生してしまうことがあります。そのエラーがLINQのメソッドを使っている際にも発生してしまうのです。

　これが結構厄介なんです。エディター上では落ちないのに、iOSの実機ではエラーになること。そして「このメソッドはエラーで落ちますよ！」ではなくて、「このメソッドのこのオーバーロードを構造体で呼ぶとエラーになりますよ！」という感じで条件が複雑なんですよね。

　１個だけ例をあげます。次のようなRecordStruct構造体があって、

```csharp:RecordStruct
public struct RecordStruct
{
    public int Score { get; set; }
}
```

　下記のコードはrecordListの要素のScoreプロパティの値の平均値を求めるコードです。これはiOSだとエラーになります。

```csharp:Averageメソッドが落ちる例
List<RecordStruct> recordStructList = GetRecordStructList ();
double averageScore = recordStructList.Average (record => record.Score);
```

　エラーログはこんな感じです。

```
Attempting to JIT compile method 'System.Linq.Enumerable:<Average`1>m__28<RecordStruct> (long,int)' while running with --aot-only.
```

　いくつかのLINQのメソッドはエラーで落ちる例や条件、そしてその解決方法・回避方法をQiitaに投稿しています。興味ある方は、よかったら読んでください。

* [【Unity】Average、Max、Min、Sum便利ですよね！けれど、【LINQ】](http://qiita.com/RyotaMurohoshi/items/d4a6750a7989fa92f81a)
* [FirstOrDefaultとかLastとかLINQ便利！けどUnity+iOSで落ちることがある、その条件と回避策](http://qiita.com/RyotaMurohoshi/items/0d4cf421e0eb4a9b4765)
* [ListからDictionary作る時もLINQを使おうぜ！ILookupも便利だぜ！](http://qiita.com/RyotaMurohoshi/items/ba4ade6c6c9dc40b6217)
* [Unity+iOSにおいて、値型のOrderByがエラーになる。その回避方法を考えて、原因を予想してみた。](http://qiita.com/RyotaMurohoshi/items/a789e6965c6de3d6e31f)

　条件次第でエラーが起こるLINQのメソッド呼び出しはありますが、大抵それを迂回するLINQ書き方があります。


# LINQ for iOSとは？

　前々節でLINQの便利さ・すごさ・すばらしさを、前節でUnity+iOSで他のプラットフォームで起きないエラーが起きてしまうことは理解していただけたでしょうか？

　ここでやっと紹介、「LINQ for iOS」の紹介です！

　「LINQ for iOS」はPatriotic Games社が作成した、カテゴリー「スクリプト」のAssetです。2014年8月28日の現時点では、価格は$5になっています。現時点でのバージョンは0.2です。(最新情報は[こちら](https://www.assetstore.unity3d.com/jp/#!/content/18131)を確認してください。)

　以下、[こちらの](https://www.assetstore.unity3d.com/jp/#!/content/18131)AssetのページよりAssetの紹介文の抜粋です。

```
There are always some people around who want to use Linq enumarable methods for their IOS games, but they always have some small AOT problems. Our team make extensions for all methods in Enumerator class. All methods in our library work on IOS dublicating LINQ calls. Feel free to use LINQ now on any mobile device.
```

　iOS上でもAOT関連のエラーが起きないLINQの互換メソッドのライブラリーですね！自分はこんなのを待っていたのです！

# 使い方

　LINQを使う際、次のような`System.Linq`のusingディレクティブを書くと思います。

```csharp:
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq; <= ここ
```

　LINQ for iOSを使う場合は、`System.Linq`を`LinqTools`に変えます。

```csharp:
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using LinqTools; <= ここ！
```

　あとは、普通にLINQのメソッドを呼び出すだけです。簡単！

# 使ってみた

　さっきのエラーが起きたコードを、LINQ for iOSを使って実行してみます！

```csharp:RecordStruct
public struct RecordStruct
{
    public int Score { get; set; }
}
```

```csharp:普通のLINQだと落ちるAverageメソッド
List<RecordStruct> recordStructList = GetRecordStructList ();
double averageScore = recordStructList.Average (record => record.Score);
```

　すばらしい！エラーがおきません。他のコードも試してみます！

```csharp:
public class RecordClass
{
	public int Score { get; set; }
}
```

　上のようなRecordClassを使った下のようなMaxメソッドは、普通のLINQだとiOSでエラーになってしまいます。

```csharp:普通のLINQだと落ちるMaxメソッド
List<RecordClass> recordClassList = GetRecordClassList ();
double averageScore = recordClassList.Max (record => record.Score);
```

ですが、LINQ for iOSを使うと、

```
Attempting to JIT compile method 'LinqTools.Enumerable:Select<RecordClass, int> (System.Collections.Generic.IEnumerable`1<RecordClass>,System.Func`2<RecordClass, int>)' while running with --aot-only.
```

　あれ、エラーが発生してしまいました！あれ、エラーが起きてしまいました！あれ、エラーが．．．(大切なことなので3回いいました。)


　どうやらいくつかのメソッドはエラーになる点が修正されているようですが、いくつかのメソッドではLINQ for iOSでもエラーになってしまうようです。

　ちなみにAverageメソッド以外にも、構造体でのLast、LastOrDefault、Single、SingleOrDefaultは修正されていて、ちゃんと実行できました。(エラーに関して、詳しくは[こちら](http://qiita.com/RyotaMurohoshi/items/0d4cf421e0eb4a9b4765))

　ToDictionary、ToLookupメソッドのエラーはなおっていないようです。(エラーに関して、詳しくは[こちら](http://qiita.com/RyotaMurohoshi/items/ba4ade6c6c9dc40b6217))

# まとめ

　LINQは非常に便利です。C#を書くのにLINQを禁じられるのは大変厳しいものがあります。ですが残念ながら、Unity+iOSではいくつかのLINQのメソッドはエラーになってしまいます。

　「LINQ for iOS」を見つけた時は、非常に嬉しかったのをよく覚えています。ですがその後、MaxメソッドやToDictionaryメソッドのエラーが直っていないことを知った時はかなりショックでした。

　いくつかのエラーは直っています。(現時点で)＄5と安価ですので、LINQ for iOSを買ってみるのも良いかもしれませんが、LINQ for iOSを使ってもエラーになるメソッドが存在するという点には注意してください。

　今後のLINQ for iOSでのバージョンアップに期待するか、他のアセットの登場を期待するか、もしくは自分で作ってしまうか。

　とりあえず結局何が言いたいかというと、

「LINQ最高！」


です。ありがとうございました。