简体   繁体   English

列表的 DataContractSerializer 失败<bool>在 Android 上使用 Unity3d 的 IL2CPP 编译时出现“未生成提前 (AOT) 代码”错误</bool>

[英]DataContractSerializer fails for List<bool> on Android when compiled using Unity3d's IL2CPP with "no ahead of time (AOT) code was generated" error

I have changed my serialization to DataContracts but now I am having problem with a specific class.我已将序列化更改为 DataContracts,但现在我遇到了特定 class 的问题。 It works fine on my Mac, but not on my android devices when built using IL2CPP.它在我的 Mac 上运行良好,但在使用 IL2CPP 构建时在我的 android 设备上运行良好。 The thread stops at the writeObject function.线程在 writeObject function 处停止。 My three classes related to the error:我与错误相关的三个类:

[DataContract]
[KnownType(typeof(TaskIdentifier))]
[KnownType(typeof(TraceableTaskItem))]
[KnownType(typeof(List<TraceableTaskItem>))]
public class TraceableTaskContainer
{
    [DataMember]
    protected TaskIdentifier _taskIdent;

    [DataMember]
    protected List<TraceableTaskItem> _lNotAccomplishedTaskItems = new List<TraceableTaskItem>();

//.....
}
[DataContract]
[KnownType(typeof(DateTime))]
[KnownType(typeof(ItemReviewStage))]
public class TraceableTaskItem : GenericTaskItem, IEquatable<TraceableTaskItem>, IComparable<TraceableTaskItem>
{
    [DataMember]
    public string sDisplayTextInTraceableTaskReport;

    [DataMember]
    protected DateTime NextReviewDate;

    [DataMember] //ItemReviewStage is a enum
    protected ItemReviewStage reviewStage = ItemReviewStage.NewTask;

   
    public TraceableTaskItem() //important to deserialize old classes, do not remove it
    {

    }
//....
}
[DataContract]
//[KnownType(typeof(List<bool>))]
abstract public class GenericTaskItem
{
    [DataMember]
    public string sItemID = "";

    //[DataMember]
    protected List<bool> lTimesAnsweredCorrectly = new List<bool>();

    protected List<List<string>> llWrongAnswers = new List<List<string>>();

//...
}

The code works with the commented lines above.该代码适用于上面的注释行。 But as soon as I uncomment DataMember on the lTimesAnsweredCorrely and with or without uncommenting the equivalent KnownType line (I have tested both), the code stops working on my mobile devices.但是,只要我在 lTimesAnsweredCorrely 上取消注释 DataMember 并且无论是否取消注释等效的 KnownType 行(我都测试过),代码就会停止在我的移动设备上运行。 Any idea how can I fix this?知道如何解决这个问题吗?

Exception:例外:

"System.Reflection.TargetInvocationException: 
Exception has been thrown by the target of an invocation. 
---> System.ExecutionEngineException: Attempting to call method \'System.Runtime.Serialization.XmlObjectSerializerWriteContext::
IncrementCollectionCountGeneric<System.Boolean>\' 
for which no ahead of time (AOT) code was generated.\n  at 
System.Reflection.MonoMethod.Invoke (System.Object obj, 
System.Reflection.BindingFlags invokeAttr, 
System.Reflection.Binder binder, System.Object[] parameters, 
System.Globalization.CultureInfo culture) [0x00000] 
in <00000000000000000000000000000000>:0 \n  at 
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] 
in <00000000000000000000000000000000>:0 \n  at System.Runtime.Serialization.XmlFormatWriterInterpreter.WriteCollection (System.Runtime.Serialization.CollectionDataContract collectionContract) [0x00000] 
in <00000000000000000000000000000000>:0 \n  at 
System.Runtime.Serialization.XmlFormatWriterInt… string



 StackTrace: "  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0 \n  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0 \n  at System.Runtime.Serialization.XmlFormatWriterInterpreter.WriteCollection (System.Runtime.Serialization.CollectionDataContract collectionContract) [0x00000] in <00000000000000000000000000000000>:0 \n  at System.Runtime.Serialization.XmlFormatWriterInterpreter.WriteCollectionToXml (System.Runtime.Serialization.XmlWriterDelegator xmlWriter, System.Object obj, System.Runtime.Serialization.XmlObjectSerializerWriteContext context, System.Runtime.Serialization.CollectionDataContract collectionContract) [0x00000] in <00000000000000000000000000000000>:0 \n  at System.Runtime.Serialization.XmlForma… string

Source: "mscorlib" string

inner exception: 
 InnerException "System.ExecutionEngineException: Attempting to call method \'System.Runtime.Serialization.XmlObjectSerializerWriteContext::
IncrementCollectionCountGeneric<System.Boolean>\' for which no ahead of time (AOT) code was generated.\n  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0 \n  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0 \n  at System.Runtime.Serialization.XmlFormatWriterInterpreter.WriteCollection (System.Runtime.Serialization.CollectionDataContract collectionContract) [0x00000] in <00000000000000000000000000000000>:0 \n  at System.Runtime.Serialization.XmlFormatWriterInterpreter.WriteCollectionToXml (System.Runtime.Serialization.XmlWriterDelegator xmlWriter, System.Object obj, System.Ru… System.Exception

Update更新

The problem seems to be with bool and int only, a List of string works just as expected.问题似乎只出在boolint上,字符串列表按预期工作。

Based on your statement根据你的说法

The problem seems to be with bool and int only, a List of string works just as expected.问题似乎只出在boolint上,字符串列表按预期工作。

It appears that, on Android, the ahead-of-time compilation for your framework isn't generating the necessary code for the .NET internal method XmlObjectSerializerWriteContext.IncrementCollectionCountGeneric<T>(XmlWriterDelegator xmlWriter, ICollection<T> collection) when T is a value type such as bool or int , but it is generating the necessary code for reference types.看来,在 Android 上,框架的提前编译没有为 .NET 内部方法XmlObjectSerializerWriteContext.IncrementCollectionCountGeneric<T>(XmlWriterDelegator xmlWriter, ICollection<T> collection) T必要的代码类型如boolint ,但它正在为引用类型生成必要的代码。 [1] [1]

This may be a bug in the runtime or framework you are using on Android.这可能是您在 Android 上使用的运行时或框架中的错误。 You may want to report an issue to the vendor.您可能想向供应商报告问题。

As a workaround , since collections such as List<string> do serialize successfully, you can create surrogate collections for your List<bool> and List<int> that appear to the serializer to be collections of strings, and perform the necessary string-to-bool (or string-to-int) conversion internally.作为一种解决方法,由于诸如List<string>之类的 collections 确实序列化成功,您可以为您的List<bool>List<int>创建代理 collections,它们在序列化程序看来是 Z0B9ABFE67CC31FCF1ZECD022EB19A52,并执行必要的 string-to1, -bool(或字符串到整数)内部转换。

First, define the following classes:首先,定义以下类:

[CollectionDataContract(Name = "ArrayOfboolean", ItemName = "boolean", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public class XmlBoolList : ConvertingList<bool, List<bool>, string>
{
    public XmlBoolList(List<bool> list) : base(() => list, b => XmlConvert.ToString(b), s => XmlConvert.ToBoolean(s)) { }
    public XmlBoolList() : this(new List<bool>()) { }
}

[CollectionDataContract(Name = "ArrayOfint", ItemName = "int", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public class XmlIntList : ConvertingList<int, List<int>, string>
{
    public XmlIntList(List<int> list) : base(() => list, i => XmlConvert.ToString(i), s => XmlConvert.ToInt32(s)) { }
    public XmlIntList() : this(new List<int>()) { }
}

public class ConvertingList<TIn, TList, TOut> : IList<TOut> where TList : IList<TIn>
{
    readonly Func<TList> getList;
    readonly Func<TIn, TOut> toOuter;
    readonly Func<TOut, TIn> toInner;

    public ConvertingList(Func<TList> getList, Func<TIn, TOut> toOuter, Func<TOut, TIn> toInner)
    {
        if (getList == null || toOuter == null || toInner == null)
            throw new ArgumentNullException();
        this.getList = getList;
        this.toOuter = toOuter;
        this.toInner = toInner;
    }

    public TList List => getList();
    TIn ToInner(TOut outer)=> toInner(outer);
    TOut ToOuter(TIn inner)=> toOuter(inner);

    #region IList<TOut> Members

    public int IndexOf(TOut item) => List.IndexOf(toInner(item));
    public void Insert(int index, TOut item) => List.Insert(index, ToInner(item));
    public void RemoveAt(int index) => List.RemoveAt(index);
    public TOut this[int index] { get => ToOuter(List[index]); set => List[index] = ToInner(value); }

    #endregion

    #region ICollection<TOut> Members

    public void Add(TOut item) => List.Add(ToInner(item));
    public void Clear() => List.Clear();
    public bool Contains(TOut item) => List.Contains(ToInner(item));
    public void CopyTo(TOut[] array, int arrayIndex)
    {
        foreach (var item in this)
            array[arrayIndex++] = item;
    }
    public int Count => List.Count;
    public bool IsReadOnly => List.IsReadOnly;
    public bool Remove(TOut item) => List.Remove(ToInner(item));

    #endregion

    #region IEnumerable<TOut> Members

    public IEnumerator<TOut> GetEnumerator()
    {
        foreach (var item in List)
            yield return ToOuter(item);
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    #endregion
}

Now modify your type GenericTaskItem contain a List<bool> by adding a private surrogate XmlBoolList property for serialization purposes:现在修改您的类型GenericTaskItem包含一个List<bool>通过添加私有代理XmlBoolList属性用于序列化目的:

abstract public class GenericTaskItem
{
    [DataMember]
    public string sItemID = "";

    [IgnoreDataMember]
    protected List<bool> lTimesAnsweredCorrectly = new List<bool>();
    
    [DataMember(Name = "lTimesAnsweredCorrectly")]
    XmlBoolList lTimesAnsweredCorrectlySurrogate { get => lTimesAnsweredCorrectly == null ? null : new XmlBoolList(lTimesAnsweredCorrectly); set => lTimesAnsweredCorrectly = value?.List; }

    //...
}

Now the serializer will serialize XmlBoolList as an array of reference types, specifically.现在,序列化程序会将XmlBoolList序列化为引用类型数组,具体来说。

Notes:笔记:

  • The XmlConvert class may be used to convert .NET primitives from and to XML values. XmlConvert class 可用于将 .NET 原语与 XML 值转换。

  • XmlBoolList and XmlIntList are designed to have the same collection data contracts as, respectively, List<bool> and List<int> . XmlBoolListXmlIntList被设计为分别具有与List<bool>List<int>相同的集合数据协定。

  • [KnownType(typeof(T))] attributes are only required when serializing polymorphic object graphs where the actual type being serialized differs from the declared type, which is not the case in your List<bool> and XmlBoolList properties. [KnownType(typeof(T))]属性仅在序列化多态 object 图时需要,其中被序列化的实际类型与声明的类型不同,而List<bool>XmlBoolList属性中的情况并非如此。 See Data Contract Known Types for details.有关详细信息,请参阅数据协定已知类型

  • You might investigate using data contract surrogates to replace collections of value types such as List<bool> globally rather than on a per-property basis.您可能会调查使用数据合约代理来替换值类型(例如List<bool> )的 collections 全局而不是基于每个属性。

Demo fiddle here .演示小提琴在这里


[1] This is surprising -- but not entirely surprising. [1]这令人惊讶——但并不完全令人惊讶。 The .NET runtime implements support for generics of reference types vs generics of value types very differently; .NET 运行时实现对 generics 的引用类型与 generics 的值类型非常不同的支持; see Generics in the Run Time (C# Programming Guide) for details.有关详细信息,请参阅运行时(C# 编程指南)中的 Generics

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM