В этом нет необходимости при использовании LayoutKind.Sequential
с преобразуемыми типами
Вам не нужно использовать отражение или какой-либо другой механизм, чтобы узнать порядок полей структуры в памяти, если все поля являются преобразовываемыми.
Преобразуемые поля для структуры, объявленной с помощью LayoutKind.Sequential
, будут находиться в памяти в том порядке, в котором поля были объявлены. Вот что значит LayoutKind.Sequential
!
Из этой документации а>:
Для непреобразуемых типов LayoutKind.Sequential управляет как макетом в управляемой памяти, так и макетом в неуправляемой памяти. Для непреобразуемых типов он управляет макетом, когда класс или структура маршалируются в неуправляемый код, но не управляет макетом в управляемой памяти.
Обратите внимание, что это не говорит вам, сколько заполнения использует каждое поле. Чтобы узнать это, смотрите ниже.
Чтобы определить порядок полей при использовании LayoutKind.Auto
или смещения полей при использовании любого макета
Довольно легко найти смещения полей структуры, если вы согласны использовать небезопасный код и не использовать отражение.
Вам просто нужно взять адрес каждого поля структуры и вычислить его смещение от начала структуры. Зная смещения каждого поля, вы можете рассчитать их порядок (и любые байты заполнения между ними). Чтобы вычислить байты заполнения, используемые для последнего поля (если есть), вам также потребуется получить общий размер структуры, используя sizeof(StructType)
.
Следующий пример работает для 32-разрядных и 64-разрядных систем. Обратите внимание, что вам не нужно использовать ключевое слово fixed
, потому что структура уже исправлена из-за того, что она находится в стеке (вы получите ошибку компиляции, если попытаетесь использовать с ней fixed
):
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();
}
}
}
Чтобы определить смещения полей при использовании LayoutKind.Sequential
Если ваша структура использует LayoutKind.Sequential
, вы можете использовать Marshal.OffsetOf()
, чтобы получить смещение напрямую , но это не работает с LayoutKind.Auto
:
foreach (var field in typeof(TestStruct).GetFields())
{
var offset = Marshal.OffsetOf(typeof (TestStruct), field.Name);
Console.WriteLine("Offset of " + field.Name + " = " + offset);
}
Это явно лучший способ сделать это, если вы используете LayoutKind.Sequential
, так как он не требует кода unsafe
и намного короче - и вам не нужно заранее знать имена полей. Как я сказал выше, нет необходимости определять порядок полей в памяти, но это может быть полезно, если вам нужно узнать, сколько используется отступов.
person
Matthew Watson
schedule
07.07.2013
LayoutKind.Sequential
управляет управляемым представлением только в том случае, если в структуре присутствуют только преобразовываемые типы. Если существует непреобразуемый тип, порядок полей в любом случае контролируется средой выполнения. Например. см. stackoverflow.com/q/14024483/11683. - person GSerg   schedule 07.07.2013