# スクリプトの制限

> Understand the restrictions that apply on some AOT platforms when using IL2CPP as your scripting back end.

Unity は、サポートしているすべてのプラットフォームで共通のスクリプティング API と体験を提供します。ただし、独自の制限があるプラットフォームもあります。これらの制限を理解するために、以下の表では、各プラットフォームとスクリプティングバックエンドに適用される制限について説明します。

| **プラットフォーム (スクリプティングバックエンド)**    | **事前 (AOT) コンパイル** | **スレッドをサポート** |
| -------------------------------- | ------------------ | ------------- |
| Android (IL2CPP)                 | はい                 | はい            |
| Android (Mono)                   | いいえ                | はい            |
| iOS (IL2CPP)                     | はい                 | はい            |
| スタンドアロン (IL2CPP)                 | はい                 | はい            |
| スタンドアロン (Mono)                   | いいえ                | はい            |
| ユニバーサル Windows プラットフォーム (IL2CPP) | はい                 | はい            |
| ウェブ (IL2CPP)                     | はい                 | いいえ           |

## 事前コンパイル (AOT)

一部のプラットフォームでは、ランタイムにコードを生成できません。ターゲットデバイスでの ジャストインタイム (JIT) コンパイルに依存するマネージコードは失敗します。代わりに、すべてのマネージコードを 事前 (AOT) にコンパイルする必要があります。多くの場合、この区別は問題になりませんが、いくつかの特定のケースでは、AOT プラットフォームでその他にも考慮事項があります。

### リフレクション

Unity は AOT プラットフォームでのリフレクションをサポートします。ただし、このコンパイラーがコードをリフレクション経由で使用していると推測できない場合、コードはランタイムに存在しない可能性があります。詳細については、[マネージコードのストリッピング](/engine/6000.0/manual/scripting/compilation-and-code-reload/backends/managed-code-stripping.md)を参照してください。

### System.Reflection.Emit

AOT プラットフォームは、`System.Reflection.Emit` 名前空間のメソッドを実装できません。

### シリアライズ

事前 (AOT) コンパイルが必要なプラットフォームでは、リフレクションの使用が原因でシリアル化と逆シリアル化で問題が発生することがあります。シリアル化と逆シリアル化の一部として、型とメソッドをリフレクション経由でのみ使用できる場合、事前コンパイラーは型とメソッドのためにコードが生成される必要があることを検知することができません。

### ジェネリック型とメソッド

汎用型とメソッドの場合、汎用インスタンスによって必要なコードが異なる場合があるため、コンパイラーはどの汎用インスタンスを使用するかを決定する必要があります。例えば、`List<int>` のコードは `List<double>` のコードとは異なります。ただし、IL2CPP は参照型の使用コードを共有するため、`List<object>` と `List<string>` に同じコードが使用されます。

以下の場合、IL2CPP がコンパイル時間を検出できなかった汎用型およびメソッド参照できます。

1. ランタイムでの新しい汎用インスタンスの作成: `Activator.CreateInstance(typeof(SomeGenericType<>).MakeGenericType(someType));`
2. 汎用インスタンスでの静的メソッドの実行: `typeof(SomeGenericType<>).MakeGenericType(someType)).GetMethod(“AMethod”).Invoke(null, null);`
3. 静的汎用メソッドの呼び出し: `typeof(SomeType).GetMethod(“GenericMethod”).MakeGenericMethod(someType).Invoke(null, null);`
4. 汎用仮想関数の呼び出しのうち、コンパイル時に推測できないものがあります。
5. `Struct<Struct<Struct<...<Struct<int>>>>` など、深くネストされた汎用値型の呼び出し。

これらのケースをサポートするために、IL2CPP は任意の型パラメーターで動作する汎用コードを生成します。ただし、このコードは型のサイズやリファレンス型か値型であるかを仮定できないため、処理が遅くなります。より高速な汎用メソッドを生成する必要がある場合は、以下のことを行います。

* 汎用引数が常にリファレンス型である場合は、`where: class` 制約を追加します。その後、IL2CPP はリファレンス型共有を使用してフォールバックメソッドを生成し、パフォーマンスの低下を起こさないようにします。
* 汎用引数が常に値型の場合は、`where: struct` 制約を追加します。これにより、いくつかの最適化が可能になりますが、値の型が異なるサイズになる可能性があるため、コードの実行は引き続き低速になります。
* `UsedOnlyForAOTCodeGeneration` という名前のメソッドを作成し、IL2CPP で生成する汎用とメソッドへの参照を追加します。このメソッドを呼び出す必要はありません (おそらく呼び出すべきではありません)。以下の例では、`GenericType<MyStruct>` の特殊化が確実に生成されます。

```lang-csharp
public void UsedOnlyForAOTCodeGeneration()
{
    // Ensure that IL2CPP will create code for MyGenericStruct
    // using MyStruct as an argument.
    new GenericType<MyStruct>();

    // Ensure that IL2CPP will create code for SomeType.GenericMethod
    // using MyStruct as an argument.
    new SomeType().GenericMethod<MyStruct>();

	public void OnMessage<T>(T value) 
	{
		Debug.LogFormat("Message value: {0}", value);
	}

    // Include an exception so we can be sure to know if this
    // method is ever called.
    throw new InvalidOperationException(
        "This method is used for AOT code generation only. " +
        "Do not call it at runtime.");
}
```

"Faster (smaller) builds" 設定を有効にすると、完全に共有可能な汎用コードのバージョンが 1 つのみコンパイルされます。これにより、生成されるメソッドの数が減り、コンパイル時間とビルドサイズが削減されますが、ランタイムパフォーマンスが犠牲になります。

### ネイティブコードからのマネージメソッドの呼び出し

ネイティブコードから呼び出せるように C 言語の関数ポインターにマーシャリングを行う必要があるマネージメソッドには、AOT プラットフォーム上でいくつかの制限があります。

* マネージメソッドは静的メソッドでなければなりません。
* マネージメソッドには `[MonoPInvokeCallback]` 属性が必要です。
* マネージメソッドが汎用の場合は、`[MonoPInvokeCallback(Type)]` オーバーロードを使用して、サポートする必要がある汎用特殊化を指定する必要があります。その場合、その型は正しい数の汎用引数を持つジェネリックインスタンスである必要があります。以下のように、メソッドに複数の `[MonoPInvokeCallback]` 属性を設定することができます。

```lang-csharp
// Generates reverse P/Invoke wrappers for NameOf<long> and NameOf<int>
// Note that the types are only used to indicate the generic arguments.
[MonoPInvokeCallback(typeof(Action<long>))]
[MonoPInvokeCallback(typeof(Action<int>))]
private static string NameOfT<T>(T item) 
{
    return typeof(T).Name;
}
```

## スレッドなし

一部のプラットフォームではスレッドの使用がサポートされないため、`System.Threading` 名前空間を使用するマネージコードはランタイムに失敗します。また、.NET クラスの一部は暗示的にスレッドに依存します。頻繁に使用される例は、スレッドのサポートに依存する `System.Timers.Timer` クラスです。

## 例外フィルター

IL2CPP は例外フィルターをサポートしますが、IL2CPP は C++ の例外を使用してマネージ例外を実装するため、実行順フィルター文と catch ブロックは異なります。フィルターがフィールドへの書き込みをブロックしない限り、これに気づくことはないでしょう。

## MarshalAs と FieldOffset 属性

IL2CPP は、`MarshalAs` 属性と `FieldOffset` 属性をランタイムに反映することをサポートしていません。IL2CPP はコンパイル時にはこれらの属性をサポートしています。これらは、適切な[プラットフォーム呼び出しマーシャリング](https://docs.microsoft.com/en-us/dotnet/framework/interop/marshaling-data-with-platform-invoke)に使用してください。

## dynamic キーワード

IL2CPP は、C# の `dynamic` キーワードをサポートしていません。このキーワードには JIT コンパイルが必要ですが、IL2CPP では不可能です。

## Marshal.Prelink

IL2CPP は `Marshal.Prelink` または `Marshal.PrelinkAll` API メソッドをサポートしません。

## System.Diagnostics.Process API

IL2CPP は `System.Diagnostics.Process` API メソッドをサポートしません。デスクトッププラットフォームでこれが必要な場合は、Mono スクリプティングバックエンドを使用します。

## 追加リソース

* [Unity .NET の機能](/engine/6000.0/manual/scripting/environment-and-tools/overview-of-dot-net-in-unity.md)
