Dictionary<string, int> 使用 protobuf-net 序列化时抛出异常

Dictionary<string, int> throwing exception when serializing with protobuf-net

我正在尝试让 protobuf-net 与 Unity 一起工作。从我最近几天所做的大量研究来看,让两者一起正常工作似乎很复杂。在使用 IL2CPP 进行构建时,我现在遇到了一个无法解决的问题。 对于某些类型,序列化似乎有时会失败,例如 Dictionary 或 Dictionary 但对于 Dictionary 可以正常工作。为了清楚起见,我使用的是 nuget 的 protobuf-net 版本 2.4.6,因为它似乎完全不支持 v3(未实现的 IL2CPP 方法)。发生的错误是:

ExecutionEngineException: Attempting to call method 'ProtoBuf.Serializers.MapDecorator`3[[System.Collections.Generic.Dictionary`2[[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.cctor' for which no ahead of time (AOT) code was generated.
  at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Reflection.MonoCMethod.DoInvoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Reflection.MonoCMethod.Invoke (System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.ValueMember.BuildSerializer () [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.ValueMember.get_Serializer () [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.MetaType.BuildSerializer () [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.MetaType.get_Serializer () [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.RuntimeTypeModel.Serialize (System.Int32 key, System.Object value, ProtoBuf.ProtoWriter dest) [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.TypeModel.SerializeCore (ProtoBuf.ProtoWriter writer, System.Object value) [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.TypeModel.Serialize (System.IO.Stream dest, System.Object value, ProtoBuf.SerializationContext context) [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Meta.TypeModel.Serialize (System.IO.Stream dest, System.Object value) [0x00000] in <00000000000000000000000000000000>:0 
  at ProtoBuf.Serializer.Serialize[T] (System.IO.Stream destination, T instance) [0x00000] in <00000000000000000000000000000000>:0 
  at SerializationTests.SerializeObject[T] (T objectToSave) [0x00000] in <00000000000000000000000000000000>:0 
  at SerializationTests.AttemptSerializingData[T] (T dataToSerialize, Entry entry) [0x00000] in <00000000000000000000000000000000>:0 
  at Entry.OnGUI () [0x00000] in <00000000000000000000000000000000>:0 

class 我正在尝试在副项目上进行序列化以进行测试,如下所示:

    using System.Collections.Generic;
    using ProtoBuf;
    
    [ProtoContract]
    public class DataDictionnary<T, U>
    {
        [ProtoMember(1)] private readonly Dictionary<T, U> someDictionnary = new Dictionary<T, U>();
    
        public DataDictionnary()
        {
    
        }
    
        public void AddValue(T key, U value)
        {
            if (someDictionnary.ContainsKey(key)) return;
    
            someDictionnary.Add(key, value);
        }
    
        public override string ToString()
        {
            string s = $"{GetType()}";
    
            foreach (var key in someDictionnary.Keys)
            {
                s += $" ({key}, {someDictionnary[key]})";
            }
    
            return s;
        }
    }

有没有理由我只能用基元序列化某些类型的字典,我可以用来让它工作的任何东西?

编辑:我的 link.xml 文件应该可以防止代码剥离

<linker>

    <assembly fullname="Test.Serialization"  preserve="all"/>

    <assembly fullname="protobuf-net"  preserve="all"/>

</linker>

SerializationTests.cs

using System.IO;
using ProtoBuf;

public static class SerializationTests
{
    public static void AttemptSerializingData<T>(T dataToSerialize, bool displayProto, Entry entry)
    {
        if (displayProto)
            entry.AddLog(Serializer.GetProto<T>());

        entry.AddLog($"Serializing {dataToSerialize}");
        var serializedObject = SerializeObject(dataToSerialize);
        entry.AddLog($"Done ({serializedObject.Length})");
        var deserializedObject = DeserializeObject<T>(serializedObject);
        entry.AddLog($"Done! {deserializedObject}");
    }

    private static T DeserializeObject<T>(byte[] bytes)
    {
        T result;

        using (var stream = new MemoryStream(bytes))
        {
            result = Serializer.Deserialize<T>(stream);
        }

        return result;
    }

    private static byte[] SerializeObject<T>(T objectToSave)
    {
        byte[] convertedObject;

        using (var stream = new MemoryStream())
        {
            Serializer.Serialize(stream, objectToSave);
            convertedObject = stream.ToArray();
        }

        return convertedObject;
    }
}

从此 GUI 代码调用 AttemptSerializingData 时抛出错误:

using System.Collections.Generic;
using UnityEngine;

public class Entry : MonoBehaviour
{
    private readonly List<string> logs = new List<string>();

    private string a = "0", b = "5", c = "-2";
    private bool displayProto;


    private void OnGUI()
    {
        displayProto = GUILayout.Toggle(displayProto, "Display proto data");

        a = GUILayout.TextField(a);
        b = GUILayout.TextField(b);
        c = GUILayout.TextField(c);

        {
            if (GUILayout.Button("Test int") && int.TryParse(a, out int _a) && int.TryParse(b, out int _b) && int.TryParse(c, out int _c))
            {
                logs.Clear();
                var data = new DataDictionnary<string, int>();
                data.AddValue("plop", _a);
                data.AddValue("pop", _b);
                data.AddValue("plp", _c);
                SerializationTests.AttemptSerializingData(data, displayProto, this);
            }
        }

        if (GUILayout.Button("Test string"))
        {
            logs.Clear();
            var data = new DataDictionnary<string, string>();
            data.AddValue("plop", a);
            data.AddValue("pop", b);
            data.AddValue("plp", c);
            SerializationTests.AttemptSerializingData(data, displayProto, this);
        }

        {
            if (GUILayout.Button("Test float") && float.TryParse(a, out float _a) && float.TryParse(b, out float _b) && float.TryParse(c, out float _c))
            {
                logs.Clear();
                var data = new DataDictionnary<string, float>();
                data.AddValue("plop", _a);
                data.AddValue("pop", _b);
                data.AddValue("plp", _c);
                SerializationTests.AttemptSerializingData(data, displayProto, this);
            }

            if (GUILayout.Button("Test int int"))
            {
                logs.Clear();
                var data = new DataDictionnary<int, int>();
                data.AddValue(0, 5);
                data.AddValue(4, 41);
                data.AddValue(2, 8);
                SerializationTests.AttemptSerializingData(data, displayProto, this);
            }

            if (GUILayout.Button("Test bool string"))
            {
                logs.Clear();
                var data = new DataDictionnary<bool, string>();
                data.AddValue(true, "ouyhgv");
                data.AddValue(false, "hoipkip");
                data.AddValue(false, "fguoii");
                SerializationTests.AttemptSerializingData(data, displayProto, this);
            }
        }

        foreach (var log in logs)
        {
            GUILayout.Label(log);
        }
    }

    public void AddLog(string text)
    {
        logs.Add(text);
    }
}

我找到了解决方案 。使用该属性声明我的所有词典可以解决问题。

[ProtoMap(DisableMap = true)]
[ProtoMember(1)]
private readonly Dictionary<string, string> _stringData;