Unity 6.3
0 онлайн 36 гостей 3 в системе
Вход

Структуры, классы и союзы

Чтобы обмениваться данными в структуре, классе или объединении, и неуправляемый, и управляемый код должны объявить совместимые структуры данных. API .NET Interop Services предоставляет атрибуты, которыми задаётся, как поля типа C# располагаются в памяти и как каждое поле маршалируется при передаче между управляемым и неуправляемым кодом.

StructLayoutAttribute позволяет привести раскладку управляемого типа в соответствие со структурой, объявленной в неуправляемом коде. По умолчанию C# размещает поля управляемых структур последовательно, исходя из размера типа каждого поля (иногда с дополнительным выравниванием). Вы можете использовать другие значения StructLayoutAttribute чтобы точно управлять смещением и фактическим размером полей структуры. Кроме того, можно задать способ маршалинга строк в структуре.

Используйте MarshalAsAttribute на отдельных полях, чтобы управлять тем, как это поле маршируется.

Использование LayoutKind.Explicit вместе с FieldOffsetAttribute чтобы объявить структуру, соответствующую Союз C/C++.

Примечание: Когда неуправляемая функция возвращает управляемому коду структуру или класс по значению, такой тип должен быть блиттабельным. (Не поддающиеся фиксации скалярные значения, такие, как bool могут быть возвращены, если вы укажете, как их распределить, как это показано в следующем примере.)

Например, следующий код на языке C описывает структуру, представляющую выровненный по осям параллелепипед, и неуправляемую функцию, принимающую эту структуру и указатель на массив в качестве параметров:

#include <math.h> // for fabsf

typedef struct _Box {
    float Position[3];
    float Scale;
    float Size[3];
} CustomBox;

extern "C" {
    bool CheckInside(CustomBox box, float* point) {
        // Use fabsf so the float differences aren't truncated to integers.
        // (The integer abs() function would round each difference toward zero.)
        if( fabsf( box.Position[0] - point[0] ) < box.Size[0] * box.Scale &&
            fabsf( box.Position[1] - point[1] ) < box.Size[1] * box.Scale &&
            fabsf( box.Position[2] - point[2] ) < box.Size[2] * box.Scale
          )
                return true;

        return false;
    }
}

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

CheckInside функция вычисляет, находится ли точка внутри параллелепипеда, и возвращает тип bool языка C. Чтобы вызвать её из управляемого кода, необходимо объявить совместимую структуру C# и сообщить среде выполнения скриптов, как обращаться с параметрами и возвращаемым значением:

using UnityEngine;
using System.Runtime.InteropServices;

// Sequential layout is the default and should be used in most cases
[StructLayout(LayoutKind.Sequential)]
public struct CustomBox
{
    // Marshall the array in place, with 3 elements
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    float[] Position;
    float Scale;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    float[] Size;

    // Constructor (defining a constructor is not mandatory)
    public CustomBox (Vector3 center, float scale, Vector3 size)
    {
        Position = new float[3];
        Size = new float[3];
        for (var i = 0; i < 3; i++)
        {
            Position[i] = center[i];
            Size[i] = size[i];
        }
        Scale = scale;
    }
}

public class StructMarshalExamples
{
    // Import the native function
    [DllImport("__Internal")]
    [return: MarshalAs(UnmanagedType.I1)] // Marshal bool as a 1 byte signed integer
    static extern bool CheckInside(CustomBox box, [In] float[] point);

    // Run the example code
    public static void RunExamples()
    {
        // box1's half-extent in each axis is Size * Scale = 1 * 2.5 = 2.5
        var box1 = new CustomBox(Vector3.zero, 2.5f, Vector3.one);
        // box2's half-extent in each axis is Size * Scale = 1 * 4 = 4
        var box2 = new CustomBox(Vector3.zero, 4, Vector3.one);
        // The x distance (2.7) is just outside box1 (2.5) but inside box2 (4).
        // If the native code truncated 2.7 to 2, box1 would wrongly report "inside".
        float[] point = { 2.7f, 1.5f, -1.5f };

        var test1 = CheckInside(box1, point);
        var test2 = CheckInside(box2, point);

        Debug.Log($"Test point is {(test1 ? "inside" : "outside")} " +
                  $"the first box and {(test2 ? "inside" : "outside")} " +
                  $"the second");
    }
}

Примечание: Измените DllImport, чтобы использовать имя библиотеки, если вы используете предкомпилированные, динамически связанные библиотеки. Используйте специальную строку, __Internal для плагинов исходного кода и статически связанных библиотек. Дополнительные сведения см. в атрибуте DllImport.