Сопоставление данных потока со структурами данных в C#

Существует ли способ сопоставления данных, собранных в потоке или массиве, со структурой данных или наоборот?
В C++ это будет просто вопрос приведения указателя на поток как тип данных, который я хочу использовать (или наоборот для обратного)
например: в C++

Mystruct * pMyStrct = (Mystruct*)&SomeDataStream;
pMyStrct->Item1 = 25;

int iReadData = pMyStrct->Item2;

очевидно, что способ C++ довольно небезопасен, если вы не уверены в качестве потоковых данных при чтении входящих данных, но для исходящих данных очень быстро и легко.

4 ответа

  1. если его .net с обеих сторон:

    думаю, вы должны использовать двоичную сериализацию и отправить результат byte [].

    доверие к вашей структуре, чтобы быть полностью blittable может быть проблемой.

    вы заплатите некоторые накладные расходы (как процессор, так и сеть), но будете в безопасности.

  2. Если вам нужно заполнить каждую переменную-член вручную, вы можете немного обобщить ее, насколько это касается примитивов, используя FormatterServices для получения списка типов переменных, связанных с объектом. Я должен был сделать это в проекте, где у меня было много различных типов сообщений, выходящих из потока, и я определенно не хотел писать сериализатор/десериализатор для каждого сообщения.

    Вот код, который я использовал для обобщения десериализации из байта[].

    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;
        }
    
  3. Большинство людей используют сериализацию .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();
    
  4. В случае, если ответ 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;
        }
    }