LoginSignup
19
18

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-08-28

この投稿は、「Unity アセット真夏のアドベントカレンダー 2014 Summer!」の8/28(木)の記事です。

前日8/27(水)の記事は、おこしさんのUnityミュージックです。
翌日8/29(金)の記事は、nanoさんのUNITY コースターです。

はじめに

 この投稿では、LINQ for iOSを紹介します。

 「Unity アセット真夏のアドベントカレンダー 2014 Summer!」では、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クラスがあって、

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

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

party
List<Player> party = GetParty ();

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

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

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

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の実機ではエラーになること。そして「このメソッドはエラーで落ちますよ!」ではなくて、「このメソッドのこのオーバーロードを構造体で呼ぶとエラーになりますよ!」という感じで条件が複雑なんですよね。

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

RecordStruct
public struct RecordStruct
{
    public int Score { get; set; }
}

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

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に投稿しています。興味ある方は、よかったら読んでください。

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

LINQ for iOSとは?

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

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

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

 以下、こちらの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ディレクティブを書くと思います。

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq; <= ここ

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

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using LinqTools; <= ここ!

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

使ってみた

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

RecordStruct
public struct RecordStruct
{
    public int Score { get; set; }
}
普通のLINQだと落ちるAverageメソッド
List<RecordStruct> recordStructList = GetRecordStructList ();
double averageScore = recordStructList.Average (record => record.Score);

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

public class RecordClass
{
    public int Score { get; set; }
}

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

普通の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は修正されていて、ちゃんと実行できました。(エラーに関して、詳しくはこちら)

 ToDictionary、ToLookupメソッドのエラーはなおっていないようです。(エラーに関して、詳しくはこちら)

まとめ

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

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

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

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

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

「LINQ最高!」

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

19
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
18