To jest niepotrzebny w przypadku korzystania LayoutKind.Sequential
z blittable typów
Nie trzeba użyć refleksji lub inny mechanizm, aby dowiedzieć się kolejność pól struct w pamięci, dopóki wszystkie pola są blittable .
Pola możliwe do wyłonienia dla struktury deklarowanej za pomocą LayoutKind.Sequential
będą przechowywane w pamięci w kolejności, w której deklarowane są pola. Oto, co oznacza LayoutKind.Sequential
!
From this documentation:
Dla blittable typów LayoutKind.Sequential kontroluje zarówno układ w zarządzanej pamięci i układu w pamięci niekontrolowana. W przypadku typów, których nie można rozcieńczyć, kontroluje układ, gdy klasa lub struktura jest przekazywana do niezarządzanego kodu, ale nie kontroluje układu w zarządzanej pamięci.
Należy pamiętać, że to nie powiedzieć, ile wyściółka każde pole jest używany. Aby się tego dowiedzieć, zobacz poniżej.
Aby ustalić kolejność pól przy użyciu LayoutKind.Auto
lub przesunięcia pól przy użyciu dowolnego układu
Jest to dość łatwe do znalezienia przesunięcia pól struct jeśli jesteś zadowolony w użyciu niebezpieczny kod, a nie użyj refleksji.
Musisz tylko podać adres każdego pola struktury i obliczyć jego przesunięcie od początku struktury. Znając przesunięcia każdego pola, możesz obliczyć ich kolejność (oraz wszelkie bajty wypełnienia między nimi).Aby obliczyć bajty wypełnienia używane w ostatnim polu (jeśli jest), musisz również uzyskać całkowity rozmiar struktury, używając sizeof(StructType)
.
Poniższy przykład działa dla wersji 32-bitowej i 64-bitowej. Należy pamiętać, że nie trzeba używać fixed
słowa kluczowego, ponieważ struktura jest już ustalona ze względu na to, że na stosie (otrzymasz błąd kompilacji, jeśli spróbujesz użyć fixed
z nim):
using System;
using System.Runtime.InteropServices;
namespace Demo
{
[StructLayout(LayoutKind.Auto, Pack = 1)]
public struct TestStruct
{
public int I;
public double D;
public short S;
public byte B;
public long L;
}
class Program
{
void run()
{
var t = new TestStruct();
unsafe
{
IntPtr p = new IntPtr(&t);
IntPtr pI = new IntPtr(&t.I);
IntPtr pD = new IntPtr(&t.D);
IntPtr pS = new IntPtr(&t.S);
IntPtr pB = new IntPtr(&t.B);
IntPtr pL = new IntPtr(&t.L);
Console.WriteLine("I offset = " + ptrDiff(p, pI));
Console.WriteLine("D offset = " + ptrDiff(p, pD));
Console.WriteLine("S offset = " + ptrDiff(p, pS));
Console.WriteLine("B offset = " + ptrDiff(p, pB));
Console.WriteLine("L offset = " + ptrDiff(p, pL));
Console.WriteLine("Total struct size = " + sizeof(TestStruct));
}
}
long ptrDiff(IntPtr p1, IntPtr p2)
{
return p2.ToInt64() - p1.ToInt64();
}
static void Main()
{
new Program().run();
}
}
}
w celu określenia przesunięcia pól przy użyciu LayoutKind.Sequential
Jeśli struct używa LayoutKind.Sequential
następnie można użyć Marshal.OffsetOf()
aby uzyskać przesunięcie bezpośrednio, ale to nie robi praca z LayoutKind.Auto
:
foreach (var field in typeof(TestStruct).GetFields())
{
var offset = Marshal.OffsetOf(typeof (TestStruct), field.Name);
Console.WriteLine("Offset of " + field.Name + " = " + offset);
}
Jest to zdecydowanie lepszy sposób, aby to zrobić, jeśli używasz LayoutKind.Sequential
, ponieważ nie wymaga kodu unsafe
, a jest znacznie krótszy - i nie musisz znać nazw pól z góry. Jak już wspomniałem powyżej, nie jest konieczne ustalanie kolejności pól w pamięci - ale może to być przydatne, jeśli trzeba się dowiedzieć, ile paddingu użyto.
Czy możesz tego nie zrozumieć, odzwierciedlając te atrybuty? –
@newStackExchangeInstance Jakie atrybuty? –
'LayoutKind.Sequential' kontroluje tylko zarządzaną reprezentację, jeśli w strukturze występują tylko typy blittable. Jeśli istnieje typ niemożliwy do zablokowania, kolejność pól jest w każdym razie kontrolowana przez środowisko wykonawcze. Na przykład. patrz http://stackoverflow.com/q/14024483/11683. – GSerg