You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#如何将自定义类转为字节数组以写入PLC缓冲区?

C# 类转PLC字节流的序列化问题解决方案

问题背景

我正在用C#开发软件,需要将由基本类型组成的泛型类转换为字节流发送至PLC缓冲区。尝试了三种方案都有问题:

  • BinaryFormatter:序列化后可还原原类,但生成的字节流包含大量元数据,找不到目标数据,且该类已被微软标记为弃用;
  • BinaryWriter:功能正常,但因存在大量不同类声明,需通过反射动态处理,实现过于复杂,暂作为备选;
  • TypeConverter:完全无效,运行时提示“TypeConverter无法转换X类”。

测试代码:

TYPE_S_TESTA test = new TYPE_S_TESTA();
test.b_tipo = 54;

using (MemoryStream ms = new MemoryStream())
{
    BinaryFormatter bf = new BinaryFormatter(); // BinaryFormatter is deprecated
    bf.Serialize(ms, test);
    ms.Seek(0, SeekOrigin.Begin);
    byte[] test3 = ms.ToArray();
}

待序列化类定义:

[Serializable]
public class TYPE_S_TESTA
{
    public short b_tipo;
    public char b_modo;
    public char b_area;
    public char b_sorgente;
    public char b_destinatario;
    public short w_lunghezza;
    public short w_contatore;
    public short w_turno;
    public short w_tempo;
}

预期得到包含值54的14字节数组,且序列化顺序与类定义一致,但实际得到512字节的缓冲区且未找到目标数据。


最优解决方案:StructLayout + Marshal 直接转换

针对PLC的字节流需求,最适合的方式是使用StructLayout特性强制成员的内存布局,再通过Marshal类直接将结构体(或类)转换为字节数组,既能保证顺序和大小精确,又无需复杂反射。

步骤1:调整数据结构与特性

由于C#默认的char是2字节UTF-16类型,而你预期的14字节总大小对应单字节的字符类型,因此需要通过MarshalAs特性指定字符按单字节处理,或直接将char改为byte类型。同时用StructLayout强制顺序和紧密排列(取消字节对齐)。

推荐将类改为结构体(更贴合PLC的C风格结构体场景):

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TYPE_S_TESTA
{
    public short b_tipo;
    // 指定char按单字节ASCII处理,对应PLC的1字节字符
    [MarshalAs(UnmanagedType.U1)]
    public char b_modo;
    [MarshalAs(UnmanagedType.U1)]
    public char b_area;
    [MarshalAs(UnmanagedType.U1)]
    public char b_sorgente;
    [MarshalAs(UnmanagedType.U1)]
    public char b_destinatario;
    public short w_lunghezza;
    public short w_contatore;
    public short w_turno;
    public short w_tempo;
}
  • LayoutKind.Sequential:强制成员按定义顺序排列,保证和PLC缓冲区顺序一致;
  • Pack = 1:取消字节对齐,避免自动添加填充字节,确保总大小为成员字节之和(25 + 14 = 14字节);
  • MarshalAs(UnmanagedType.U1):将C#的双字节char转为单字节,匹配PLC的字符类型。

步骤2:转换为字节数组

使用Marshal类的方法直接将结构体转换为字节数组:

TYPE_S_TESTA test = new TYPE_S_TESTA();
test.b_tipo = 54;

// 获取结构体的字节大小
int bufferSize = Marshal.SizeOf(typeof(TYPE_S_TESTA));
byte[] plcBuffer = new byte[bufferSize];

// 分配非托管内存并复制结构体数据
IntPtr unmanagedPtr = Marshal.AllocHGlobal(bufferSize);
try
{
    Marshal.StructureToPtr(test, unmanagedPtr, false);
    Marshal.Copy(unmanagedPtr, plcBuffer, 0, bufferSize);
}
finally
{
    // 释放非托管内存
    Marshal.FreeHGlobal(unmanagedPtr);
}

// plcBuffer即为14字节的目标字节流,顺序与结构体定义一致,包含54的二进制值

方案优势

  • 精确控制:完全匹配PLC的字节顺序和大小要求,无额外元数据;
  • 高效简洁:无需反射或复杂的手动序列化逻辑,适合大量不同结构的场景;
  • 安全可靠:避免使用已弃用的BinaryFormatter,不存在安全风险。

其他方案问题说明

  • BinaryFormatter:它序列化的是.NET完整对象,包含类型元数据、版本信息等额外内容,因此生成的字节流远大于实际数据大小,且无法直接用于PLC缓冲区;
  • TypeConverter:仅用于类型间的逻辑转换(如字符串转数值),不适合二进制序列化场景;
  • BinaryWriter:若后续需要使用,可结合反射封装通用序列化方法,但复杂度远高于本方案。

内容的提问来源于stack exchange,提问作者Samuele Stefanoni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 05:27:25