3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-01-23

##動かなくなったので共有
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クラスで良いのでは???

3
3
1

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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?