Обязательно ли использовать FieldOffset для каждого члена класса / структуры, если он вообще используется? - PullRequest
2 голосов
/ 27 января 2020

Рассмотрим случай, когда мне нужно убедиться, что класс / структура отображается в память очень специфичным c способом, вероятно, из-за необходимости соответствия внешнему протоколу:

[StructLayout(LayoutKind.Sequential, Pack=1)]
public class SYSTEM_INFO
{
 public ulong OemId;
 public ulong PageSize;
 public ulong ActiveProcessorMask;
 public ulong NumberOfProcessors;
 public ulong ProcessorType;
}

Тогда я подумал о том, чтобы сделать «оверлей» (это правильный термин?), чтобы я мог получить прямой доступ к памяти:

[StructLayout(LayoutKind.Explicit)]
public class SYSTEM_INFO
{
[FieldOffset(0)] public byte[] Buffer = new byte[40]; //overlays all the bytes, like a C union
[FieldOffset(0)] public ulong OemId;
[FieldOffset(8)] public ulong PageSize;
[FieldOffset(16)] public ulong ActiveProcessorMask;
[FieldOffset(24)] public ulong NumberOfProcessors;
[FieldOffset(32)] public ulong ProcessorType;
}

Но это отнимает много времени и подвержено ошибкам (если что-то изменится, я могу легко испортить) обновление всех FieldOffset значений) - и было указано, что оно действительно недействительно по причинам, которые я не до конца понимаю:

Необработанное исключение. System.TypeLoadException: не удалось загрузить тип 'SYSTEM_INFO2' из сборки 'a2bbzf3y.exe, версия = 0.0.0.0, Culture = нейтральный, PublicKeyToken = null', поскольку он содержит поле объекта со смещением 0, которое неправильно выровнено или перекрыто не -объект Команда завершается сигналом 6

Возможно ли выполнить комбинацию обоих подходов, когда Buffer перекрывает элементы, но все элементы , за исключением Buffer, автоматически, последовательно выровнен без дополнения? Я не могу разобрать из документов, если это разрешено или нет, и я могу принудительно смещать некоторые члены только с автоматически выбранными другими.

1 Ответ

2 голосов
/ 27 января 2020

В случае struct вы можете сделать это довольно легко - и, честно говоря, если вы смотрите на байты, вам следует обычно использовать struct:

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

static class P
{
    static void Main()
    {
        var obj = new SystemInfo();
        var bytes = AsBytes(ref obj);
        Console.WriteLine(bytes.Length); // 40
    }

    static Span<byte> AsBytes<T>(ref T value)
        => MemoryMarshal.CreateSpan(
            ref Unsafe.As<T, byte>(ref value),
            Unsafe.SizeOf<T>());
}

public readonly struct SystemInfo
{
    public readonly ulong OemId;
    public readonly ulong PageSize;
    public readonly ulong ActiveProcessorMask;
    public readonly ulong NumberOfProcessors;
    public readonly ulong ProcessorType;
}
...