C# 具有动态大小数组的封送结构

C# 具有动态大小数组的封送结构,c#,serialization,struct,marshalling,C#,Serialization,Struct,Marshalling,我在使用动态大小数组编组结构时遇到问题。定长阵列结构简单;加上 [MarshalAs(UnmanagedType.ByValArray, SizeConst = TheSizeOfTheArray)] 然而,当涉及到动态大小的数组时,我就不知所措了。为了简单起见,我将省略代码中所有不相关的内容 我将这个序列化结构发送到的设备需要一个ushort来通知数组的长度,后面是数组本身,最后是一个CRC [StructLayout(LayoutKind.Sequential, Pack = 1, Cha

我在使用动态大小数组编组结构时遇到问题。定长阵列结构简单;加上

[MarshalAs(UnmanagedType.ByValArray, SizeConst = TheSizeOfTheArray)]
然而,当涉及到动态大小的数组时,我就不知所措了。为了简单起见,我将省略代码中所有不相关的内容

我将这个序列化结构发送到的设备需要一个ushort来通知数组的长度,后面是数组本身,最后是一个CRC

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct MyNetworkMessage
{
    public ushort Length { get; }

    // This attribute does not work as I had hoped
    [MarshalAs(UnmanagedType.ByValArray, SizeParamIndex = 1)]
    private byte[] _messageData;

    public ushort Crc { get; private set; }

    public byte[] MessageData
    {
        get { return _data; }
        private set { _data = value; }
    }

    public MyNetworkMessage(byte[] data)
    {
        MessageData = data;
        Length = (ushort)data.Length;
        Crc = Helper.CalcCrc(MessageData);
    }
}
此结构需要序列化为字节数组,该数组通过线路发送到另一个设备,其中前两个字节是数组的长度,后两个字节是MessageData的CRC:

Byte 0..1       Length of Data-field, N
Byte 2..N+2     Data
Byte N+3..N+4   CRC
我有许多类似的不同结构,需要序列化并作为字节数组通过线路发送,所以我想要的是一种通用的处理方法。为这个示例创建一个正确的字节数组非常简单,但我不想为每个结构编写序列化/反序列化,因为它们都只包含简单的数据


我在这里看到了前面提出的类似问题,标记为重复,但没有看到任何令人满意的答案。

您可以编写自己的简单序列化逻辑。您还可以编写自己的属性,用于修饰要序列化的字段

下面是一个完整的可编译控制台应用程序,它演示了这一思想

注意它如何创建一个新的属性类,
NetworkSerialisationAttribute
,并使用它来修饰可序列化字段。它还使用反射来确定要序列化的字段

此示例代码仅支持字节数组和基元类型,但应该足以让您开始学习。(它也只支持序列化字段,而不支持属性,并且不进行反序列化——但根据您所说的,我认为这就足够了。)

这里的想法是避免编写大量重复的序列化代码。相反,您只需使用
[networkserialization]
属性来告诉它要序列化什么

注意,这里的大多数代码只编写一次;然后,您可以将其放入库中,并将其用于所有数据传输类型。例如,下面代码中的
MyNetworkMessage
MyOtherNetworkMessage
表示数据传输类型

using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication2
{
    public enum SerialisationKind
    {
        Scalar,
        Array
    }

    [MetadataAttribute]
    public sealed class NetworkSerialisationAttribute: Attribute
    {
        public NetworkSerialisationAttribute(int ordinal, SerialisationKind kind = SerialisationKind.Scalar)
        {
            _ordinal = ordinal;
            _kind = kind;
        }

        public SerialisationKind Kind // Array or scalar?
        {
            get
            {
                return _kind;
            }
        }

        public int Ordinal // Defines the order in which fields should be serialized.
        {
            get
            {
                return _ordinal;
            }
        }

        private readonly int _ordinal;
        private readonly SerialisationKind _kind;
    }

    public static class NetworkSerialiser
    {
        public static byte[] Serialise<T>(T item)
        {
            using (var mem = new MemoryStream())
            {
                serialise(item, mem);
                mem.Flush();
                return mem.ToArray();
            }
        }

        private static void serialise<T>(T item, Stream output)
        {
            var fields = item.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            var orderedFields = 
                from    field in fields
                let     attr = field.GetCustomAttribute<NetworkSerialisationAttribute>()
                where   attr != null
                orderby attr.Ordinal
                select  new { field, attr.Kind };

            foreach (var info in orderedFields)
            {
                if (info.Kind == SerialisationKind.Array)
                    serialiseArray(info.field.GetValue(item), output);
                else
                    serialiseScalar(info.field.GetValue(item), output);
            }
        }

        private static void serialiseArray(object value, Stream output)
        {
            var array = (byte[])value; // Only byte arrays are supported. This throws otherwise.

            ushort length = (ushort) array.Length;
            output.Write(BitConverter.GetBytes(length), 0, sizeof(ushort));
            output.Write(array, 0, array.Length);
        }

        private static void serialiseScalar(object value, Stream output)
        {
            if (value is byte) // Byte is a special case; there is no BitConverter.GetBytes(byte value)
            {
                output.WriteByte((byte)value);
                return;
            }

            // Hacky: Relies on the underlying type being a primitive type supported by one
            // of the BitConverter.GetBytes() overloads.

            var bytes = (byte[])
                typeof (BitConverter)
                .GetMethod("GetBytes", new [] {value.GetType()})
                .Invoke(null, new[] {value});

            output.Write(bytes, 0, bytes.Length);
        }
    }

    // In this class, note the use of the [NetworkSerialization] attribute to indicate
    // which fields should be serialised.

    public sealed class MyNetworkMessage
    {
        public MyNetworkMessage(byte[] data)
        {
            _data = data;
            _crc = 12345; // You should use Helper.CalcCrc(data);
        }

        public ushort Length
        {
            get
            {
                return (ushort)_data.Length;
            }
        }

        public ushort Crc
        {
            get
            {
                return _crc;
            }
        }

        public byte[] MessageData
        {
            get
            {
                return _data;
            }
        }

        [NetworkSerialisation(0, SerialisationKind.Array)]
        private readonly byte[] _data;

        [NetworkSerialisation(1)]
        private readonly ushort _crc;
    }

    // In this struct, note how the [NetworkSerialization] attribute is used to indicate the
    // order in which the fields should be serialised.

    public struct MyOtherNetworkMessage
    {
        [NetworkSerialisation(5)]  public int Int1;
        [NetworkSerialisation(6)]  public int Int2;

        [NetworkSerialisation(7)]  public long Long1;
        [NetworkSerialisation(8)]  public long Long2;

        [NetworkSerialisation(3)]  public byte Byte1;
        [NetworkSerialisation(4)]  public byte Byte2;

        [NetworkSerialisation(9)]  public double Double1;
        [NetworkSerialisation(10)] public double Double2;

        [NetworkSerialisation(1)]  public short Short1;
        [NetworkSerialisation(2)]  public short Short2;

        public float ThisFieldWillNotBeSerialised;

        public string AndNeitherWillThisOne;
    }

    class Program
    {
        private static void Main(string[] args)
        {
            var test1 = new MyNetworkMessage(new byte[10]);

            var bytes1 = NetworkSerialiser.Serialise(test1);

            Console.WriteLine(bytes1.Length + "\n");

            var test2 = new MyOtherNetworkMessage
            {
                Short1  = 1,
                Short2  = 2,
                Byte1   = 3,
                Byte2   = 4,
                Int1    = 5,
                Int2    = 6,
                Long1   = 7,
                Long2   = 8,
                Double1 = 9,
                Double2 = 10
            };

            var bytes2 = NetworkSerialiser.Serialise(test2);
            Console.WriteLine(bytes2.Length);

            foreach (byte b in bytes2)
            {
                Console.WriteLine(b);
            }
        }
    }
}
使用系统;
使用System.ComponentModel.Composition;
使用System.IO;
使用System.Linq;
运用系统反思;
命名空间控制台应用程序2
{
公共枚举SerialisationKind
{
标量,
排列
}
[元数据属性]
公共密封类NetworkSerialisationAttribute:属性
{
公用网络SerialIsationAttribute(int-ordinal,SerialisationKind=SerialisationKind.Scalar)
{
_序数=序数;
_善良=善良;
}
public SerialisationKind//数组还是标量?
{
得到
{
回报;;
}
}
public int Ordinal//定义字段序列化的顺序。
{
得到
{
返回顺序;
}
}
私有只读整型序号;
私有只读SerialisationKind\u kind;
}
公共静态类NetworkSerialiser
{
公共静态字节[]序列化(T项)
{
使用(var mem=new MemoryStream())
{
序列化(项目,mem);
mem.Flush();
返回mem.ToArray();
}
}
私有静态void序列化(T项,流输出)
{
var fields=item.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var orderedFields=
从田野到田野
让attr=field.GetCustomAttribute()
其中attr!=null
按属性序号排序
选择新{field,attr.Kind};
foreach(orderedFields中的var信息)
{
if(info.Kind==SerialisationKind.Array)
serialiseArray(info.field.GetValue(项),输出);
其他的
serialiseScalar(info.field.GetValue(项),输出);
}
}
私有静态void serialiseArray(对象值、流输出)
{
var array=(byte[])value;//只支持字节数组。否则将抛出。
ushort length=(ushort)数组.length;
output.Write(位转换器.GetBytes(长度),0,sizeof(ushort));
输出.写入(数组,0,数组.长度);
}
私有静态void serialiseScalar(对象值、流输出)
{
if(value is byte)//byte是一种特殊情况;没有位转换器。GetBytes(byte value)
{
output.WriteByte((字节)值);
返回;
}
//Hacky:依赖于基础类型是一个
//GetBytes()重载的。
变量字节=(字节[])
类型(位转换器)
.GetMethod(“GetBytes”,新[]{value.GetType()})
.Invoke(null,新[]{value});
输出.写入(字节,0,字节.长度);
}
}
//在此类中,请注意使用[NetworkSerialization]属性来指示
//哪些字段应该序列化。
公共密封类MyNetworkMessage
{
公共MyNetworkMessage(字节[]数据)
{
_数据=数据;
_crc=12345;//应该使用Helper.CalcCrc(数据);
}
公共短距离
{
得到
{
返回(ushort)\数据长度;
}
}
公共卫生服务中心
{
得到
{
返回crc;
}
}
公众的