获得二进制序列化类的大小的好方法

时间:2009-12-10 20:02:19

标签: c# serialization

我正在编写一些将用于与C#TCP服务器通信的类,我正在使用BinaryWriterBinaryReader编写序列化/反序列化方法。这很容易,但我遇到的情况是我有一个类Class1,里面包含一个实例Class2。这两个类都实现了一个定义方法byte[] ToBytes()void FromBytes(byte[] data)的接口。所以我有这个代码:

public class Class1
{
    public int Action;
    public Class2 Data;

    public void FromBytes(byte[] data)
    {
        BinaryReader br = new BinaryReader(new MemoryStream(data));
        Action= br.ReadInt32();
        Data = new Class2();
        Data.FromBytes(br.ReadBytes(___));
        br.Close();
    }
}

问题是,ReadBytes电话会怎样?除了硬编码实现此接口的任何对象的大小之外,我不确定是否可以这样做,但是有更优雅的东西吗?我考虑过使用反射来计算它在运行时(为未来的调用缓存它),但这看起来更加丑陋。

编辑:我应该指出,如果数据是可变长度的,那么二进制表示包括必要的数据长度,并且解串器将处理它。

4 个答案:

答案 0 :(得分:2)

也许将实例的大小放在实例数据的开头?虽然,如果您使用BinaryReader,您应该能够将阅读器本身传递给Class2对象中的方法,并且可以使用它来反序列化二进制数据。

答案 1 :(得分:2)

通常情况下,我会传递byte[](可能是约束流,以阻止它在我们预期的区域之外读取)或包装阅读器,而不是传递Stream,并使用它 - 即让课程阅读所需内容。

另一个简单的选择是在序列化期间对数据进行长度前缀,以便您事先知道大小。

或者,节省了很多麻烦并使用预先滚动的二进制序列化框架。有几个......他们都必须解决这个问题。很高兴(这是我的宠物区)。

我也担心你班上的问题分离,但这是一个副作用。

答案 2 :(得分:2)

如果我正确理解了您的问题,并且没有说明您解决此问题的方法的正确性, Marshal.SizeOf 应该可以解决您的问题。

我冒昧地填写了代码中的空白:


class Program
{
    [StructLayout(LayoutKind.Sequential)]
    public class Class2
    {
        public int Data;
        public int SomeMoreData;

        internal void FromBytes(byte[] p)
        {
            BinaryReader br = new BinaryReader(new MemoryStream(p));
            Data = br.ReadInt32();
            SomeMoreData = br.ReadInt32();
            br.Close();
        }
    }

    public class Class1
    {
        public int Action;
        public Class2 Data;

        public void FromBytes(byte[] data)
        {
            BinaryReader br = new BinaryReader(new MemoryStream(data));
            Action = br.ReadInt32();
            Data = new Class2();
            int sizeOfClass2Instance = Marshal.SizeOf(Data);
            Data.FromBytes(br.ReadBytes(sizeOfClass2Instance));
            br.Close();
        }
    }

    static void Main(string[] args)
    {
        Class1 c1 = new Class1();

        int[] data = new int[3] { 1, 2, 3 };
        byte[] dataInBytes = new byte[data.Length * sizeof(int)];

        for (int i=0;i<data.Length;i++)
        {
            byte[] src = BitConverter.GetBytes(data[i]);
            Array.Copy(src, 0, dataInBytes, i * sizeof(int), sizeof(int));
        }
        c1.FromBytes(dataInBytes);
    }
}

诀窍是,Marshal.SizeOf依赖于你的类的StructLayout属性。您必须明确说明数据在类中的存储方式。

答案 3 :(得分:1)

如果您确定Class2的每个对象在使用ToBytes时都提供相同数量的字节,请创建Class2的虚拟对象并使用ToBytes()。Length。

或者只是:

    Data = new Class2();
    Data.FromBytes(br.ReadBytes(Data.ToBytes().Length));

(显然,你可以通过存储Data.ToBytes()。长度在某处并重复使用来提高效率。)