繁体   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

我已将序列化更改为 DataContracts,但现在我遇到了特定 class 的问题。 它在我的 Mac 上运行良好,但在使用 IL2CPP 构建时在我的 android 设备上运行良好。 线程在 writeObject function 处停止。 我与错误相关的三个类:

[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>>();

//...
}

该代码适用于上面的注释行。 但是,只要我在 lTimesAnsweredCorrely 上取消注释 DataMember 并且无论是否取消注释等效的 KnownType 行(我都测试过),代码就会停止在我的移动设备上运行。 知道如何解决这个问题吗?

例外:

"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

更新

问题似乎只出在boolint上,字符串列表按预期工作。

根据你的说法

问题似乎只出在boolint上,字符串列表按预期工作。

看来,在 Android 上,框架的提前编译没有为 .NET 内部方法XmlObjectSerializerWriteContext.IncrementCollectionCountGeneric<T>(XmlWriterDelegator xmlWriter, ICollection<T> collection) T必要的代码类型如boolint ,但它正在为引用类型生成必要的代码。 [1]

这可能是您在 Android 上使用的运行时或框架中的错误。 您可能想向供应商报告问题。

作为一种解决方法,由于诸如List<string>之类的 collections 确实序列化成功,您可以为您的List<bool>List<int>创建代理 collections,它们在序列化程序看来是 Z0B9ABFE67CC31FCF1ZECD022EB19A52,并执行必要的 string-to1, -bool(或字符串到整数)内部转换。

首先,定义以下类:

[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
}

现在修改您的类型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; }

    //...
}

现在,序列化程序会将XmlBoolList序列化为引用类型数组,具体来说。

笔记:

  • XmlConvert class 可用于将 .NET 原语与 XML 值转换。

  • XmlBoolListXmlIntList被设计为分别具有与List<bool>List<int>相同的集合数据协定。

  • [KnownType(typeof(T))]属性仅在序列化多态 object 图时需要,其中被序列化的实际类型与声明的类型不同,而List<bool>XmlBoolList属性中的情况并非如此。 有关详细信息,请参阅数据协定已知类型

  • 您可能会调查使用数据合约代理来替换值类型(例如List<bool> )的 collections 全局而不是基于每个属性。

演示小提琴在这里


[1]这令人惊讶——但并不完全令人惊讶。 .NET 运行时实现对 generics 的引用类型与 generics 的值类型非常不同的支持; 有关详细信息,请参阅运行时(C# 编程指南)中的 Generics

暂无
暂无

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

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