"Warning: implicit downcast from Object to int" の対症療法と根治

# 症状

```as3:例
var array : Array = [1, 2, 3];
var elem : int = array[1];
```

UnityScriptで`Array`から`int`を取り出そうとしたりすると、以下の様な警告に悩まされます。

    Warning: implicit downcast from Object to int

# 原因

intを[UnityScriptのArray][array]に入れてしまうと、`int`が[ボックス化][arrayboo]されてしまい、要素を取り出す時はすべて`Object`扱いになります。そのため、取り出す際にはなんらかの変換が必要になります。

# 対症療法1: 取り出してからintに変換

UnityScriptの`as`演算子によるキャストは参照型専用のため、`array[1] as int`と書いてすませることはできません。

代用として、.NET標準の[System.Convert.ToInt32][toint32]が使えます。これで`Object`を`int`に変換できます。

```as3:ToInt32で変換
var array : Array = [1, 2, 3];
var elem : int = System.Convert.ToInt32(array[1]);
```

# 対症療法2: ArrayごとList.&lt;int&gt;に変換

より安全なのは、Arrayの代わりに要素の型を一種類に固定したコンテナを使うことです。ここではすでにArrayがあるものとして、変換の方法を挙げていきます。

`List`の[コンストラクタ][listctor]が変換に便利です。機能もほぼArrayの上位互換です。

```as3:List.&lt;int&gt;に変換
using System.Collections.Generic;
// ...
var array : Array = [1, 2, 3];
var listOfInts : List.<int> = new List.<int>(array);
var elem : int = listOfInts[1];
```

# 対症療法3: Arrayごとint[]に変換

`int[]`も意外と機能は豊富です。伸び縮みこそできないものの、[System.Array][dotnetarray]のスタティックメソッド群は頼りになります。

## for文で変換

`for`文を回してコピーする場合は、対症療法1の`System.Convert.ToInt32`が必要になります。目的と手段が逆になっている気もしますが、これが最もメモリが少なくすみます。

```as3:for文でコピー
var array : Array = [1, 2, 3];
var ints : int[] = new int[array.length];
for (var i = 0; i < ints.length; i++) {
  ints[i] = System.Convert.ToInt32(array[i]);
}
var elem : int = ints[1];
```

## ToBuiltinで変換 (使えたら)

[Unityのリファレンス][array]には、`ToBuiltin`というそのもののメソッドがあります。しかし、プリミティブ型では[うまく動かない][tobuiltinsucks]ようです。

```as3:実行時にInvalidCastExceptionが起こる
var array : Array = [1, 2, 3];
var listOfInts : int[] = array.ToBuiltin(int) as int[]; 
var elem : int = listOfInts[1];
```

## List#ToArrayで変換

富豪的には、`List`を経由して[ToArray][toarray]を使うのが記述が短くすみます。

```as3:List#ToArrayで変換
var array : Array = [1, 2, 3];
var listOfInts : int[] = new List.<int>(array).ToArray();
var elem : int = listOfInts[1];
```

# 根治: Arrayを使わない

以上に紹介した方法はいずれも対症療法です。`Array`を受け渡ししないのが安全性的にも速度的にも一番です。ほとんどの場合は`List.<int>`で置き換えることができます。

筆者の場合は手を抜いてブラウザ用のJSONパーサを持ってきた時にこの警告に遭遇しました。その後[Json.NET](http://james.newtonking.com/json)に乗り換えました。

# 他の原因

`Array`を使わない時でも、以下のような記述をすると`implicit downcast`の警告が出ます。

```as3:型宣言のつけ忘れ
var array : int[] = [1, 2, 3];
var elem;
elem = array[1];
var foo : int = elem;
```

これは変数`elem`の型がデフォルトの`Object`になったことによるものです。`var elem : int;`と宣言しておくことで警告も速度悪化も避けることができます。他の方法として、`var elem = array[1];`のように宣言と初期化を同時に行うと[型推論が働くので][perf]記述を短くすますことが出来ます。

他にこの警告が出た例があれば教えて下さい。

# リンク

* [Unity - Scripting API: Array][array]
* [System.Convert.ToInt32 - MSDN][toint32]
* [Array メソッド (System) - MSDN][dotnetarray]
* [List.ToArray メソッド (System.Collections.Generic) - MSDN][toarray]
* [Unity Script Reference - Overview: Performance Optimization][perf]
* [unityscript/Array.boo at master - bamboo/unityscript][arrayboo]
[perf]: http://docs.unity3d.com/412/Documentation/ScriptReference/index.Performance_Optimization.html
[toint32]: http://msdn.microsoft.com/en-us/library/system.convert.toint32.aspx
[array]: docs.unity3d.com/ScriptReference/Array.html
[dotnetarray]: http://msdn.microsoft.com/ja-jp/library/System.Array_methods(v=vs.80).aspx
[tobuiltinsucks]: http://d.hatena.ne.jp/shinriyo/20120822/p1
[toarray]: http://msdn.microsoft.com/ja-jp/library/x303t819(v=vs.80).aspx
[listctor]: http://msdn.microsoft.com/ja-jp/library/fkbw11z0(v=vs.80).aspx
[arrayboo]: https://github.com/bamboo/unityscript/blob/master/src/UnityScript.Lang/Array.boo