Переинтерпретировать необработанный буфер C # в структуру blittable - PullRequest
1 голос
/ 27 мая 2019

Я ищу способ переосмыслить произвольную небезопасную область памяти как структуру blittable в C #.Вот неудачная попытка, которую я мог написать до сих пор:

[StructLayout(LayoutKind.Explicit, Size = sizeof(int))]
struct Foo
{
    [FieldOffset(0)]
    public int X;
}

public static unsafe void Main()
{
    // Allocate the buffer
    byte* buffer = stackalloc byte[sizeof(Foo)];

    // A tentative of reinterpreting the buffer as the struct
    Foo foo1 = Unsafe.AsRef<Foo>(buffer);

    // Another tentative to reinterpret as the struct
    Foo foo2 = *(Foo*) buffer;

    // Get back the address of the struct
    void* p1 = &foo1;
    void* p2 = &foo2;

    Console.WriteLine(new IntPtr(buffer).ToString("X"));
    Console.WriteLine(new IntPtr(p1).ToString("X"));
    Console.WriteLine(new IntPtr(p2).ToString("X"));
}

Тем не менее, все адреса в печатной памяти разные (я ожидал, что будет напечатан один и тот же адрес).Первая попытка использует Unsafe.AsRef<T>(..), предоставленный Microsoft, где описание метода гласит:

Повторно интерпретирует данное местоположение как ссылку на значение типа T.

Я не уверен, почему переосмысление здесь не выполняется должным образом.

Любой совет?

1 Ответ

2 голосов
/ 27 мая 2019

Реинтерпретация работает как задумано, причина различий в адресах состоит в том, что эти две строки создают независимые копии данных в двух новых переменных:

Foo foo1 = Unsafe.AsRef<Foo>(buffer);
// ...
Foo foo2 = *(Foo*) buffer;

Чтобы избежать копирования, вы можете объявить переменные как ref локальные (начиная с C # 7) или указатели:

byte* buffer = ...

// Option 1, pointers only
Foo* foo1 = (Foo*)buffer;
foo1->X = 123;

// Option 2, ref local cast from pointer    
ref Foo foo2 = ref *(Foo*)buffer;
foo2.X = 456;

// Option 3, ref local with Unsafe.AsRef
// Unlike option 2 this also allows reinterpreting non-blittable types,
// but in most cases that's probably undesirable
ref Foo foo3 = ref Unsafe.AsRef<Foo>(buffer);
foo3.X = 789;
...