对此没有太多解释,这就是我所拥有的:
public struct PACKET_HEADER
{
public string computerIp;
public string computerName;
public string computerCustomName;
};
public struct PACKET
{
public PACKET_HEADER pktHdr;
public PACKET_DATA pktData;
};
public struct PACKET_DATA
{
public Command command;
public string data;
};
public struct DATA_MESSAGE
{
public string message;
};
public struct DATA_FILE
{
public string fileName;
public long fileSize;
};
基本上我希望PACKET_DATA中的数据字段能够是DATA_FILE或DATA_MESSAGE。我知道类型需要改变,但我不知道该怎么做,泛型是一种选择吗?
最终结果应该是我可以做到的:
pktData.data.fileName 要么 pktData.data.message
修改
我能做到:
public struct PACKET_DATA
{
public Command command;
public string data;
public DATA_MESSAGE data_message;
public DATA_FILE data_file;
};
并且只要我不需要它们就将data_message或文件设置为null?这将如何影响序列化/字节数组和发送的数据。如果我使用课程,我会遇到同样的问题
编辑2
public struct PACKET_MESSAGE
{
public PACKET_HEADER pktHdr;
public Command command;
public DATA_MESSAGE pktData;
};
public struct PACKET_FILE
{
public PACKET_HEADER pktHdr;
public Command command;
public DATA_FILE pktData;
};
编辑3
我有一个消毒器和消毒器,可以使用我原来的例子,如果没有打嗝,那么实际的序列化就完成了。
编辑4
除了我的序列化程序正在“尝试读取或写入受保护的内存”之外,一切似乎都在起作用。这通常表明其他内存已损坏。 Gunna在发布我的工作解决方案时会看一下它:)编辑5
public static byte[] Serialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdatas = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
IntPtr buffer = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(anything, buffer, false);
handle.Free();
return rawdatas;
}
public static object Deserialize(byte[] rawdatas, Type anytype)
{
int rawsize = Marshal.SizeOf(anytype);
if (rawsize > rawdatas.Length)
return null;
GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
IntPtr buffer = handle.AddrOfPinnedObject();
object retobj = Marshal.PtrToStructure(buffer, anytype);
handle.Free();
return retobj;
}
FINAL
结构:
public struct PACKET_HEADER
{
public string computerIp;
public string computerName;
public string computerCustomName;
};
public struct PACKET
{
public PACKET_HEADER pktHdr;
public PACKET_DATA pktData;
};
public struct PACKET_DATA
{
public Command command;
public IDATA data;
public T GetData<T>() where T : IDATA
{
return (T)(data);
}
}
public interface IDATA { }
public struct DATA_MESSAGE : IDATA
{
public string message;
}
public struct DATA_FILE : IDATA
{
public string fileName;
public long fileSize;
}
如何创建新数据包(可能可以将tbh组合在一起):
public static PACKET CreatePacket(Command command)
{
PACKET packet;
packet.pktHdr.computerIp = Settings.ComputerIP;
packet.pktHdr.computerName = Settings.ComputerName;
packet.pktHdr.computerCustomName = Settings.ComputerCustomName;
packet.pktData.command = command;
packet.pktData.data = null;
return packet;
}
public static PACKET CreatePacket(Command command, DATA_MESSAGE data_message)
{
PACKET packet;
packet.pktHdr.computerIp = Settings.ComputerIP;
packet.pktHdr.computerName = Settings.ComputerName;
packet.pktHdr.computerCustomName = Settings.ComputerCustomName;
packet.pktData.command = command;
packet.pktData.data = data_message;
return packet;
}
public static PACKET CreatePacket(Command command, DATA_FILE data_file)
{
PACKET packet;
packet.pktHdr.computerIp = Settings.ComputerIP;
packet.pktHdr.computerName = Settings.ComputerName;
packet.pktHdr.computerCustomName = Settings.ComputerCustomName;
packet.pktData.command = command;
packet.pktData.data = data_file;
return packet;
}
(de)上面的序列化。
简单示例:
PACKET packet = Packet.CreatePacket(command, data_file);
byte[] byData = Packet.Serialize(packet);
另一端:
PACKET returnPacket = (PACKET)Packet.Deserialize(socketData.dataBuffer, typeof(PACKET));
// Get file
string fileName = returnPacket.pktData.GetData<DATA_FILE>().fileName;
long fileSize = returnPacket.pktData.GetData<DATA_FILE>().fileSize;
一切似乎都很好用而花花公子:)
答案 0 :(得分:3)
这个问题需要一个明确的答案,所以我会试着总结一下:
如果你想采用C#数据结构并将其转换为字节数组,你可以使用结构和编组,或者使用类(或结构,但为什么会这样)和序列化框架(如{{1} }},或自定义序列化逻辑(与BinaryFormatter
一样)。我们可以讨论哪个更好,但我们现在假设我们使用结构,我们正在使用Marshaling。虽然我会说,结构非常有限,并且应该主要用于与Win32 API函数互操作。
所以问题是,我们有一个容器结构,它可能包含两种类型的子结构中的一种。如果你要使用一个结构元素,那么诸如泛型和/或使用子结构类型的通用接口之类的东西就不会飞。基本上你唯一的选择是让容器有两个结构和一个bool标志,指示要使用哪个结构。这有增加数据包大小的缺点,因为你也发送了未使用的子结构。
在目前的情况下,结果如下:
BinaryWriter
那就是说,在你的情况下使用结构和编组只能在你自己的进程中工作,因为你的结构包含字符串。当struct包含指向非fixedlength字符串的指针时,这些字符串将在其他地方分配,并且不会是您复制的字节数组的一部分,只有指向它们的指针。您还需要在某个时刻调用Marshal.DestroyStructure,并将IntPtr传递给StructureToPtr,以便清理这些字符串资源。
这个故事的寓意:你能不能做出你最初提出的结论:是的。如果你像以前一样使用它们:不。因为你有一个可变大小的数据结构,你试图通过网络发送(我假设结构被称为PACKET),结构不起作用,你真的需要使用某种序列化框架或自定义序列化逻辑。
答案 1 :(得分:1)
public struct PACKET_DATA
{
public IData data;
public T GetData<T>() where T : IDATA
{
return (T)data;
}
}
public interface IDATA { }
public struct DATA_MESSAGE : IDATA
{
public string message;
}
public struct DATA_FILE : IDATA
{
public string fileName;
public long fileSize;
}
PACKET_DATA packetData = new PACKET_DATA();
packetData.data = new DATA_MESSAGE();
var message = packetData.GetData<DATA_MESSAGE>().message;
答案 2 :(得分:0)
你可以用泛型来做,但是然后一个类型参数会传播到PACKET结构,我猜这会使你的工作变得尴尬,而不是你想要的。
在这里使用结构的目的是什么,而不是类?是互操作吗? (在这种情况下,互操作方案将决定正确的解决方案)。还是要避免装箱/堆分配?
答案 3 :(得分:0)
如何定义两个结构继承的虚假接口?当然,这不会解决您的序列化问题,但正如所说,您可能还需要自定义序列化方法。
public interface IDataType
{
}
public struct PACKET_DATA
{
public Command command;
public IDataType data;
};
public struct DATA_MESSAGE : IDataType
{
public string message;
};
public struct DATA_FILE : IDataType
{
public string fileName;
public long fileSize;
};