تعيين بيانات الدفق إلى هياكل البيانات في C#

StackOverflow https://stackoverflow.com/questions/2256

  •  08-06-2019
  •  | 
  •  

سؤال

هل هناك طريقة لتعيين البيانات التي تم جمعها على دفق أو صفيف إلى بنية البيانات أو العكس؟في C ++ ، سيكون هذا ببساطة مسألة إلقاء مؤشر إلى الدفق كنوع بيانات أريد استخدامه (أو العكس للعكس للعكس) على سبيل المثال:في سي ++

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

int iReadData = pMyStrct->Item2;

من الواضح أن طريقة C++ غير آمنة إلى حد كبير إلا إذا كنت متأكدًا من جودة بيانات الدفق عند قراءة البيانات الواردة، ولكن بالنسبة للبيانات الصادرة فهي سريعة وسهلة للغاية.

هل كانت مفيدة؟

المحلول

يستخدم معظم الأشخاص تسلسل .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();

نصائح أخرى

في حالة أن إجابة لوبوس هاسكو لم تكن آمنة بما فيه الكفاية، فهناك أيضًا حقًا طريقة غير آمنة ، باستخدام مؤشرات في 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;
    }
}

إذا كان .net على كلا الجانبين:

أعتقد أنه يجب عليك استخدام التسلسل الثنائي وإرسال نتيجة البايت [].

إن الثقة في البنية الخاصة بك لتكون قابلة للاختراق بالكامل يمكن أن تكون مشكلة.

ستدفع بعض النفقات العامة (كل من وحدة المعالجة المركزية والشبكة) ولكنها ستكون آمنة.

إذا كنت بحاجة إلى تعبئة كل متغير عضو يدويًا، فيمكنك تعميمه قليلًا فيما يتعلق بالأوليات باستخدام 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;
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top