C# 结构中的结构,可以更改内部结构类型

C# 结构中的结构,可以更改内部结构类型,c#,struct,C#,Struct,对此没有太多的解释,这就是我所拥有的: public struct PACKET_HEADER { public string computerIp; public string computerName; public string computerCustomName; }; public struct PACKET { public PACKET_HEADER pktHdr;

对此没有太多的解释,这就是我所拥有的:

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;       
    };
基本上,我希望数据包数据中的数据字段能够是数据文件或数据消息。我知道类型需要更改,但我不知道该做什么,泛型是一种选择吗

最终结果应该是,我可以执行以下任一操作:

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_消息或文件设置为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;
    } 
决赛

结构:

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;
}
(反)上述序列化

简单的例子:

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;
packetreturnpacket=(PACKET)PACKET.Deserialize(socketData.dataBuffer,typeof(PACKET));
//获取文件
字符串文件名=returnPacket.pktData.GetData().fileName;
long fileSize=returnPacket.pktData.GetData().fileSize;

一切似乎都很顺利:)

您可以使用泛型,但随后一个类型参数将传播到数据包结构,我猜这将使其难以使用,而不是您想要的

在这里使用结构而不是类的目的是什么?是互操作吗?(在这种情况下,互操作场景将指示正确的解决方案)。还是为了避免装箱/堆分配?

public struct PACKET\u数据
    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;
{ 公共IData数据; public T GetData(),其中T:IDATA { 返回(T)数据; } } 公共接口IDATA{} 公共结构数据_消息:IDATA { 公共字符串消息; } 公共结构数据_文件:IDATA { 公共字符串文件名; 公共长文件大小; } PACKET_DATA packetData=新的PACKET_DATA(); packetData.data=新数据_消息(); var message=packetData.GetData().message;
如何定义两个结构都从中继承的伪接口?当然,这不会解决序列化问题,但正如前面所说的,您可能仍然需要自定义序列化方法

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;       
};

这个问题需要一个明确的答案,所以我将尝试总结:

如果您想将C#数据结构转换为字节数组,可以使用结构和封送,或者使用类(或结构,但为什么要使用)和序列化框架(如
BinaryFormatter
),或者自定义序列化逻辑(如
BinaryWriter
)。我们可以就哪一个更好展开讨论,但现在假设我们使用的是结构,我们使用的是封送。尽管我会说,结构是非常有限的,应该主要用于与Win32 API函数的互操作

所以问题是,我们有一个容器结构,它可能包含两种类型的子结构之一。如果你要封送一个结构,像泛型和/或为你的子结构类型使用一个公共接口这样的事情是不会发生的。基本上,您唯一的选择是让容器同时具有structs和bool标志,指示要使用哪种结构。这样做的缺点是会增加数据包的大小,因为您也会发送未使用的子结构

在本例中,结果如下所示:

public struct PACKET_DATA
{
    public Command command;
    public string data;
    public bool is_message_packet;
    public DATA_MESSAGE data_message;
    public DATA_FILE data_file;
};
也就是说,在您的情况下,使用结构和编组只会在您自己的流程中真正起作用,因为您的结构包含字符串。当结构包含指向非固定长度字符串的指针时,这些字符串将分配到其他位置,并且不会成为您复制的字节数组的一部分,只有指向它们的指针才会被分配。您还需要在某个时候调用Marshal.DestroyStructure,使用传递给StructureToPtr的IntPtr来清理这些字符串资源


所以这个故事的寓意是:你能按照你最初的要求制作结构吗。你应该像现在这样使用它们:不。因为你有一个可变大小的数据结构,你正试图通过网络发送(我假设,因为这个结构被称为数据包),所以结构不起作用,你真的需要使用某种序列化框架或自定义序列化逻辑

“你可以用泛型来做,但是类型参数会传播到数据包结构中,我猜这会让你很难使用它,而不是你想要的。”是的,我看过了,但它确实变得非常混乱。数据包结构(及其内容)获取序列化并作为字节数组通过网络发送。其目的只是为了在短时间内存储少量信息,我了解到结构比类更适合于此。主要是因为您希望高效序列化吗?如果是这样,最佳方法是编写自定义序列化器。使这些类型类而不是结构(就GC而言)不是很好。只是澄清一下你的编辑:结构,作为值类型,永远不会为空。你需要一个标志来指示哪个成员是有效的。是的,我刚刚意识到它们不能为空。我想有两种类型的数据包,见上文,这会更有意义吗?好的,但请记住反序列化的代码需要确定t他键入。我实际上喜欢你的两个子结构版本,你只需要一个
bool isMessagePacket;
。关于你的内存访问错误,你有什么方法
public struct PACKET_DATA
{
    public Command command;
    public string data;
    public bool is_message_packet;
    public DATA_MESSAGE data_message;
    public DATA_FILE data_file;
};