Asignación de datos de Stream a estructuras de datos en C #

votos
20

¿Hay alguna manera de mapear los datos recopilados en una secuencia o matriz a una estructura de datos o viceversa? En C ++ esto simplemente sería una cuestión de lanzar un puntero a la secuencia como un tipo de datos que quiero usar (o viceversa para el reverso), por ejemplo: en C ++

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

int iReadData = pMyStrct->Item2;

Obviamente, la forma C ++ es bastante insegura a menos que esté seguro de la calidad de los datos de transmisión al leer los datos entrantes, pero que los datos salientes sean muy rápidos y fáciles.

Publicado el 05/08/2008 a las 14:11
fuente por usuario
En otros idiomas...                            


4 respuestas

votos
16

La mayoría de las personas usa la serialización de .NET (hay un formateador XML más rápido binario y más lento, ambos dependen del reflejo y son tolerantes a la versión en cierto grado)

Sin embargo, si quiere la forma más rápida (insegura), ¿por qué no?

Escritura:

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();

Leyendo:

handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
o = (YourStruct)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(YourStruct));
handle.Free();
Respondida el 05/08/2008 a las 16:46
fuente por usuario

votos
5

En caso de que la respuesta de lubos hasko no fuera lo suficientemente segura, también existe la forma realmente insegura, usando punteros en C #. Aquí hay algunos consejos y trampas con los que me he encontrado:

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;
    }
}
Respondida el 24/10/2009 a las 12:37
fuente por usuario

votos
2

Si necesita completar manualmente cada variable miembro, puede generalizarla un poco en cuanto a las primitivas mediante el uso de FormatterServices para recuperar en orden la lista de tipos de variables asociados con un objeto. Tuve que hacer esto en un proyecto en el que tenía muchos tipos diferentes de mensajes saliendo de la transmisión y definitivamente no quería escribir el serializador / deserializador para cada mensaje.

Aquí está el código que utilicé para generalizar la deserialización de un 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;
    }
Respondida el 05/08/2008 a las 14:47
fuente por usuario

votos
2

si es .net en ambos lados:

cree que debería usar la serialización binaria y enviar el resultado del byte [].

confiar en que tu estructura sea completamente blittable puede ser un problema.

pagará algunos gastos generales (tanto de la CPU como de la red) pero estará seguro.

Respondida el 05/08/2008 a las 14:29
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more