MessagePack for C#で、Genericな変数をIL2CPP環境で使いたいときのメモ

##動かなくなったので共有
2.3系(?)ではでUnionの仕様が変わったらしく、下記は使えなくなりました。
具体的に言うと、UnionがInterfaceか抽象クラスにしか対応しなくなったので、
下記の手法を使おうとすると、Unityエディタでの動作確認時点でException発生するようになります。
うまく対応させる方法があるのかは現在模索中です。

##前提
このライブラリの応用？的な使い方の話をしています。
現時点の最新版、2.2.85で動作確認済み。
https://github.com/neuecc/MessagePack-CSharp

Unityエディタでは動いてたのに、IL2CPP対応(iOSとかWebGLビルドだと強制）で
FormatterNotRegisteredException 出てキッツい人向けお役立ち情報。

##やりたいこと
変数部分をGenericにして、要処で使いわけたいパターンがあるとする。
テストコードとしてはこういう感じ。

```.cs
public class TestClass
{
    [MessagePackObject(keyAsPropertyName: true)]
    public class TargetClass<T>
    {
        public T param = default;
        public TargetClass(T param)
        {
            this.param = param;
        }
    }

    // Tには [MessagePackObject] が付いた別クラスのインスタンスが来る想定
    public void ExecuteTest<T>(T parameter)
    {
        var bytes = MessagePackSerializer.Serialize(new TargetClass<T>(parameter));
        MessagePackSerializer.Deserialize<TargetClass<T>>(bytes);
    }
}
```

Unityで通常実行すると普通に通るんだけども、何も考えずIL2CPP設定でビルドすると通らない。
IL2CPP環境では動的コード生成が行えないので、事前にGeneratedResolverを生成しておく必要がある。
事前生成のやり方は、公式ReadMeのAOT Code Generation (support for Unity/Xamarin)の項目で説明されている。
https://github.com/neuecc/MessagePack-CSharp#aot-code-generation-support-for-unityxamarin

__問題は、AOTコード生成をやっても上のコードはまだ通らない。__
なので、もうひと手間を加える必要があるぞ、というのが今回の記事。

##解決方法
```.cs
    [MessagePackObject(keyAsPropertyName: true)]
    [MessagePack.Union(0, typeof(TargetClass<GenericClass_0>))]
    [MessagePack.Union(1, typeof(TargetClass<GenericClass_1>))]
    [MessagePack.Union(2, typeof(TargetClass<GenericClass_2>))]// 対応させる必要クラス分、Unionをこの要領で追加すること
    public class TargetClass<T>
    {
        public T param = default;
        public TargetClass(T param)
        {
            this.param = param;
        }
    }
```
Union機能を使うことで解決ができます。
https://github.com/neuecc/MessagePack-CSharp#union
__Unionを追加した後に、先述のAOT Code Generationの作業を(再)実行する必要がある__ので注意してください。
AOT Code Generationで生成したコード内を見ると、対応できてそうかは目視確認できます。

```.cs
    // 前略
    internal static class GeneratedResolverGetFormatterHelper
    {
        private static readonly global::System.Collections.Generic.Dictionary<Type, int> lookup;

        static GeneratedResolverGetFormatterHelper()
        {
            lookup = new global::System.Collections.Generic.Dictionary<Type, int>(193)
            {
                // 中略
                { typeof(global::TestClass.TargetClass<global::GenericClass_0>), 30 },
                { typeof(global::TestClass.TargetClass<global::GenericClass_1>), 31 },
                { typeof(global::TestClass.TargetClass<global::GenericClass_2>), 32 },
                // 後略
```

こういう感じで、TargetClass\<T\> 項目の反映がなされていれば多分OKです。

###追記(2021/01/26)
一応ですが、これはバッドノウハウに相当する可能性があります。

Unionを追加した状態でAOT Code Generation手順を忘れると
UnityエディタでDynamicUnionResolver（非IL2CPP環境で、対応コードを自動生成してくれるやつ）が
```Union can only be interface or abstract class.```というエラーを吐きます。
エラー表示通り、本来Unionはinterfaceとabstractクラスだけを対応するつもりのものっぽいですね。
後々の仕様変更で死ぬ可能性はあるので、その際はまた別途解決策を模索したほうがよさそうです。

Unityエディタで暫定回避をするための対応としては、下記みたいな方法があるんじゃないでしょうか。
- Union部分を```#if ENABLE_IL2CPP```とか```#if !UNITY_EDITOR```で括って、発生を回避する
- 逆に何もせず、エラーを都度出して見落とさないようにし、AOTコード生成を都度行う運用でカバー

###追記2(2021/01/27)
今思ったんだけどこれ、GenericじゃなくてAbstractクラスで良いのでは？？？