Как я могу поместить массив внутри структуры в C #? - PullRequest
2 голосов
/ 27 марта 2009

C ++ код:

struct tPacket
{
    WORD word1;
    WORD word2;
    BYTE byte1;
    BYTE byte2;
    BYTE array123[8];
}

static char data[8192] = {0};
...
some code to fill up the array
...
tPacket * packet = (tPacket *)data;

Мы не можем сделать это так просто в C #.

Обратите внимание, что в структуре C ++ есть массив.

В качестве альтернативы, использование этого исходного файла может сделать за нас работу, но не при наличии массива в структуре.

Ответы [ 4 ]

8 голосов
/ 27 марта 2009

Я не уверен точно, что вы спрашиваете. Вы пытаетесь получить эквивалентное определение структуры в C # для простого старого использования C # или для взаимодействия (PInvoke)? Если это для PInvoke, следующая структура будет работать

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct tPacket {

    /// WORD->unsigned short
    public ushort word1;

    /// WORD->unsigned short
    public ushort word2;

    /// BYTE->unsigned char
    public byte byte1;

    /// BYTE->unsigned char
    public byte byte2;

    /// BYTE[8]
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=8, ArraySubType=System.Runtime.InteropServices.UnmanagedType.I1)]
    public byte[] array123;
}

Если вы ищете простую старую структуру C # с такими же характеристиками, к сожалению, это невозможно сделать со структурой. Вы не можете определить встроенный массив размера contstant в структуре C #, а также не можете заставить массив иметь определенный размер через инициализатор.

В управляемом мире есть два альтернативных варианта.

Используйте структуру, которая имеет метод create, заполняющий массив

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct tPacket {
    public ushort word1;
    public ushort word2;
    public byte byte1;
    public byte byte2;
    public byte[] array123;
    public static tPacket Create() { 
      return new tPacket() { array123 = new byte[8] };
    }
}

Или, в качестве альтернативы, используйте класс, в котором вы можете напрямую инициализировать переменную-член array123.

РЕДАКТИРОВАТЬ OP хочет знать, как преобразовать байт [] в значение tPacket

К сожалению, нет отличного способа сделать это в C #. C ++ был великолепен для такого рода задач, потому что имеет очень слабую систему типов, в которой вы можете выбрать просмотр потока байтов в качестве определенной структуры (приведение к дурному указателю).

Это может быть возможно в небезопасном коде C #, но я не верю, что это так.

По сути, вам нужно вручную разобрать байты и присвоить их различным значениям в структуре. Или напишите собственный метод, который выполняет приведение стиля C и PInvoke в эту функцию.

1 голос
/ 01 марта 2012

Вы можете поместить то, что выглядит во внешний мир как массив фиксированного размера, в безопасную структуру, написав функции в структуре для доступа. Например, вот фиксированный массив двойной точности 4 на 4 в безопасной структуре:

public struct matrix4 //  4 by 4 matrix  
{  
    //  
    //  Here we will create a square matrix that can be written to and read from similar  
    //  (but not identical to) using an array.  Reading and writing into this structure  
    //  is slower than using an array (due to nested switch blocks, where nest depth  
    //  is the dimensionality of the array, or 2 in this case).  A big advantage of this  
    //  structure is that it operates within a safe context.  
    //  
    private double a00; private double a01; private double a02; private double a03;  
    private double a10; private double a11; private double a12; private double a13;  
    private double a20; private double a21; private double a22; private double a23;  
    private double a30; private double a31; private double a32; private double a33;  
    //
    public void AssignAllZeros()                    //  Zero out the square matrix  
    { /* code */}               
    public double Determinant()                     //  Common linear algebra function  
    { /* code */}  
    public double Maximum()                         //  Returns maximum value in matrix  
    { /* code */}  
    public double Minimum()                         //  Minimum value in matrix  
    { /* code */}  
    public double Read(short row, short col)        //  Outside read access   
    { /* code */}  
    public double Read(int row, int col)            //  Outside read access overload  
    { /* code */}  
    public double Sum()                             //  Sum of 16 double precision values  
    {  
        return a00 + a01 + a02 + a03 + a10 + a11 + a12 + a13 + a20 + a21 + a22 + a23 + a30 + a31 + a32 + a33;  
    }  
    public void Write(short row, short col, double doubleValue)  //  Write access to matrix  
    { /* code */}  
    public void Write(int row, int col, double doubleValue)      //  Write access overload  
    { /* code */}              
}
1 голос
/ 27 марта 2009

Это может быть сделано и с небезопасным кодом, хотя он ограничивает контекст, в котором может выполняться ваша программа, и, естественно, вносит возможность ошибок безопасности. Преимущество состоит в том, что вы преобразуете непосредственно из массива в структуру с помощью указателей, и это также не требует технического обслуживания, если вы только собираетесь добавлять или удалять поля из структуры. Однако для доступа к массивам требуется использование оператора fixed, поскольку GC может перемещать структуру в памяти, когда она содержится в объекте.

Вот некоторый модифицированный код небезопасной структуры, которую я использовал для интерпретации пакетов UDP:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public unsafe struct UnsafePacket
{
    int time;
    short id0;
    fixed float acc[3];
    short id1;
    fixed float mat[9];

    public UnsafePacket(byte[] rawData)
    {
        if (rawData == null)
            throw new ArgumentNullException("rawData");
        if (sizeof(byte) * rawData.Length != sizeof(UnsafePacket))
            throw new ArgumentException("rawData");

        fixed (byte* ptr = &rawData[0])
        {
            this = *(UnsafePacket*)rawPtr;
        }
    }

    public float GetAcc(int index)
    {
        if (index < 0 || index >= 3)
            throw new ArgumentOutOfRangeException("index");
        fixed (UnsafePacket* ptr = &acc)
        {
            return ptr[index];
        }
    }

    public float GetMat(int index)
    {
        if (index < 0 || index >= 9)
            throw new ArgumentOutOfRangeException("index");
        fixed (UnsafePacket* ptr = &mat)
        {
            return ptr[index];
        }
    }

            // etc. for other properties
}

Для этого вида кода чрезвычайно важно проверить, что длина массива точно соответствует размеру структуры, иначе вы откроете для некоторых неприятных переполнений буфера. Поскольку ключевое слово unsafe было применено ко всей структуре, вам не нужно отмечать каждый метод или кодовый блок как отдельные операторы unsafe .

1 голос
/ 27 марта 2009

Я думаю, что то, что вы ищете (если вы используете определение структуры, подобное опубликованному JaredPar), выглядит примерно так:

tPacket t = new tPacket();
byte[] buffer = new byte[Marshal.SizeOf(typeof(tPacket))];
socket.Receive(buffer, 0, buffer.length, 0);

GCHandle pin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
t = (tPacket)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(tPacket));
pin.free();

//do stuff with your new tPacket t
...