【问题标题】:C# serialization failure - workarounds?C# 序列化失败 - 解决方法?
【发布时间】:2015-04-22 18:19:46
【问题描述】:

下面的程序导致以下故障:

Field in TypedReferences cannot be static or init only

基于此,明显的解决方法是:

  1. 移除readonly 修饰符private readonly T _value

但是,以下两个更改也消除了故障:

  1. struct Data 更改为class Data
  2. public int[] Content 更改为public int Content

有谁知道 BinaryFormatter 内部发生了什么,允许序列化在案例 #2 和 #3 中继续进行?


using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializationStumper {

    // *** helper

    public static class Serializer {
        public static MemoryStream SerializeToStream<T>(T o)
        {
            var stream = new MemoryStream();
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, o);
            return stream;
        }

        public static T DeserializeFromStream<T>(MemoryStream stream)
        {
            IFormatter formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }

        public static T RoundTrip<T>(T v) {
            return DeserializeFromStream<T>(SerializeToStream<T>(v));
        }
    }

    // *** set up data structures

    [Serializable]
    public struct Wrapped<T> {
        private readonly T _value;

        public Wrapped(T value) {
            _value = value;
        }   
    }

    [Serializable]
    public struct Data {
        public int[] Content;
    }

    // *** Perform test

    public class Program {
        private static void Main(string[] args)
        {
            var wrapped = new Wrapped<Data>(new Data { Content = new int[] { 1, 2, 3 } } ); 
            var roundtrip = Serializer.RoundTrip(wrapped);  
            Console.WriteLine(roundtrip);
        }
    }
}

【问题讨论】:

  • 完整的堆栈跟踪告诉我们代码在这里失败:[ArgumentException: Field in TypedReferences cannot be static or init only.] System.TypedReference.MakeTypedReference(Object target, FieldInfo[] flds) 和其中抛出它的具体检查方法是: if (runtimeFieldInfo.IsInitOnly || runtimeFieldInfo.IsStatic) throw new ArgumentException(Environment.GetResourceString("Argument_TypedReferenceInvalidField"));所以我认为类和结构之间的区别与它们如何通过 ObjectManager 中的解析加载有关。即不同的属性
  • 可能是为结构而不是类调用 Serialization.ObjectManager.DoValueTypeFixup。 DoValueTypeFixup 依次调用 TypedReference.MakeTypedReference。我无法逐步浏览源代码,但是如果您加载符号并这样做,它应该会变得清晰(呃)。

标签: c# serialization binary readonly binaryformatter


【解决方案1】:

既然还没人回答,那我就刺一下……

序列化正常,但反序列化失败。

我不确定,但我怀疑这是因为作为一个结构,反序列化器不知道这个数组有多大:

    public int[] Content;

所以它不知道要读取多少字节来填充数组。 作为一个类,它可能会将长度序列化为数据。

如果您同时将结构体和类序列化为不同的文件,它们的内容确实略有不同,序列化类中的附加字节之一是数字 3,我猜它是数组长度。虽然我还没有研究过二进制输出格式。

“修复”可能与fixed size buffers 有关,以便反序列化器知道数据流的多少字节组成数组。不过,我还没有制定出使用它们的可行解决方案。

edit:看来你不能序列化一个固定大小的缓冲区,所以忽略上面的。

所以#2 可以工作,因为它将数组长度序列化到数据流中,而#3 可以工作,因为int 是一个固定的字节大小。

【讨论】:

  • 数组长度被序列化到结构和类的流中,所以这不会是原因:#2 当数据是类与结构时,文件中的额外长度只是类与结构的标头长度差异。即结构 020000000200000005 和类 02000000020000000903000000050300000015 的这个。如果你改变数组中的元素数量,这些标题都不会改变。包括数组长度在内的信息是为 Content 属性编码的,并且它的序列化字节对于 struct vs class 不会改变。
  • A 3元件阵列被编码如下:07080200000009040000000F0400000003000000080100000002000000030000000B和4元件阵列这样07080200000009040000000F040000000400000008010000000200000003000000040000000B其中000F040000000300 VS 000F040000000400具有长度编码,并且如果你改变了结构VS类不改变跨度>
猜你喜欢
  • 2016-06-15
  • 2013-09-04
  • 2021-08-28
  • 2012-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-28
  • 1970-01-01
相关资源
最近更新 更多