将流数据映射到C#中的数据结构
时间:2020-03-05 18:37:43 来源:igfitidea点击:
有没有一种方法可以将在流或者数组上收集的数据映射到数据结构,反之亦然?
在C ++中,这仅是将指向流的指针转换为我要使用的数据类型的问题(反之亦然)
例如:在C ++中
Mystruct * pMyStrct = (Mystruct*)&SomeDataStream; pMyStrct->Item1 = 25; int iReadData = pMyStrct->Item2;
显然,除非我们在读取传入数据时确定流数据的质量,否则C ++方法是非常不安全的,但是对于传出数据而言,这是非常快速和容易的。
解决方案
回答
如果双方的.net:
认为我们应该使用二进制序列化并发送byte []结果。
信任结构完全可破解可能会很麻烦。
我们将需要支付一些开销(cpu和网络),但是这样做是安全的。
回答
如果我们需要手动填充每个成员变量,则可以使用FormatterServices按顺序检索与对象关联的变量类型的列表,就其涉及的原语进行概括。我必须在一个项目中执行此操作,在该项目中,我会遇到很多不同的消息类型,并且我绝对不想为每条消息编写序列化器/反序列化器。
这是我用来从byte []进行反序列化的代码。
public virtual bool SetMessageBytes(byte[] message) { MemberInfo[] members = FormatterServices.GetSerializableMembers(this.GetType()); object[] values = FormatterServices.GetObjectData(this, members); int j = 0; for (int i = 0; i < members.Length; i++) { string[] var = members[i].ToString().Split(new char[] { ' ' }); switch (var[0]) { case "UInt32": values[i] = (UInt32)((message[j] << 24) + (message[j + 1] << 16) + (message[j + 2] << 8) + message[j + 3]); j += 4; break; case "UInt16": values[i] = (UInt16)((message[j] << 8) + message[j + 1]); j += 2; break; case "Byte": values[i] = (byte)message[j++]; break; case "UInt32[]": if (values[i] != null) { int len = ((UInt32[])values[i]).Length; byte[] b = new byte[len * 4]; Array.Copy(message, j, b, 0, len * 4); Array.Copy(Utilities.ByteArrayToUInt32Array(b), (UInt32[])values[i], len); j += len * 4; } break; case "Byte[]": if (values[i] != null) { int len = ((byte[])values[i]).Length; Array.Copy(message, j, (byte[])(values[i]), 0, len); j += len; } break; default: throw new Exception("ByteExtractable::SetMessageBytes Unsupported Type: " + var[1] + " is of type " + var[0]); } } FormatterServices.PopulateObjectMembers(this, members, values); return true; }
回答
大多数人使用.NET序列化(有更快的二进制文件和更慢的XML格式化程序,它们都依赖于反射并且在一定程度上可以容忍版本)
但是,如果我们想要最快(不安全)的方式,为什么不这样做:
写作:
YourStruct o = new YourStruct(); byte[] buffer = new byte[Marshal.SizeOf(typeof(YourStruct))]; GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); Marshal.StructureToPtr(o, handle.AddrOfPinnedObject(), false); handle.Free();
读:
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); o = (YourStruct)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(YourStruct)); handle.Free();
回答
如果lubos hasko的回答还不够安全,还有一种真正不安全的方法,即使用
C#中的指针。这是我遇到的一些提示和陷阱:
using System; using System.Runtime.InteropServices; using System.IO; using System.Diagnostics; // Use LayoutKind.Sequential to prevent the CLR from reordering your fields. [StructLayout(LayoutKind.Sequential)] unsafe struct MeshDesc { public byte NameLen; // Here fixed means store the array by value, like in C, // though C# exposes access to Name as a char*. // fixed also requires 'unsafe' on the struct definition. public fixed char Name[16]; // You can include other structs like in C as well. public Matrix Transform; public uint VertexCount; // But not both, you can't store an array of structs. //public fixed Vector Vertices[512]; } [StructLayout(LayoutKind.Sequential)] unsafe struct Matrix { public fixed float M[16]; } // This is how you do unions [StructLayout(LayoutKind.Explicit)] unsafe struct Vector { [FieldOffset(0)] public fixed float Items[16]; [FieldOffset(0)] public float X; [FieldOffset(4)] public float Y; [FieldOffset(8)] public float Z; } class Program { unsafe static void Main(string[] args) { var mesh = new MeshDesc(); var buffer = new byte[Marshal.SizeOf(mesh)]; // Set where NameLen will be read from. buffer[0] = 12; // Use Buffer.BlockCopy to raw copy data across arrays of primitives. // Note we copy to offset 2 here: char's have alignment of 2, so there is // a padding byte after NameLen: just like in C. Buffer.BlockCopy("Hello!".ToCharArray(), 0, buffer, 2, 12); // Copy data to struct Read(buffer, out mesh); // Print the Name we wrote above: var name = new char[mesh.NameLen]; // Use Marsal.Copy to copy between arrays and pointers to arrays. unsafe { Marshal.Copy((IntPtr)mesh.Name, name, 0, mesh.NameLen); } // Note you can also use the String.String(char*) overloads Console.WriteLine("Name: " + new string(name)); // If Erik Myers likes it... mesh.VertexCount = 4711; // Copy data from struct: // MeshDesc is a struct, and is on the stack, so it's // memory is effectively pinned by the stack pointer. // This means '&' is sufficient to get a pointer. Write(&mesh, buffer); // Watch for alignment again, and note you have endianess to worry about... int vc = buffer[100] | (buffer[101] << 8) | (buffer[102] << 16) | (buffer[103] << 24); Console.WriteLine("VertexCount = " + vc); } unsafe static void Write(MeshDesc* pMesh, byte[] buffer) { // But byte[] is on the heap, and therefore needs // to be flagged as pinned so the GC won't try to move it // from under you - this can be done most efficiently with // 'fixed', but can also be done with GCHandleType.Pinned. fixed (byte* pBuffer = buffer) *(MeshDesc*)pBuffer = *pMesh; } unsafe static void Read(byte[] buffer, out MeshDesc mesh) { fixed (byte* pBuffer = buffer) mesh = *(MeshDesc*)pBuffer; } }