Размер управляемых структур - PullRequest
12 голосов
/ 24 января 2010

.NET 4.0 Framework представляет классы для чтения и записи отображенных в память файлов . Классы сосредоточены вокруг методов чтения и написания структур . Они не распределяются, а копируются из файла и в файл в том виде, в котором они размещены в управляемой памяти.

Допустим, я хочу записать две структуры последовательно в отображенный в память файл, используя следующие методы:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Foo
{
    public char C;
    public bool B;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bar
{
}

static void Write<T1, T2>(T1 item1, T2 item2)
    where T1 : struct
    where T2 : struct
{
    using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32))
    using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
    {
        accessor.Write<T1>(0L, ref item1);  //  <-- (1)
        accessor.Write<T2>(??, ref item2);  //  <-- (2)
    }
}

static void Main()
{
    Foo foo = new Foo { C = 'α', B = true };
    Bar bar = new Bar { };
    Write(foo, bar);
}

Как получить число байтов, записанных в (1), чтобы я мог написать следующее значение рядом с (2)?

Примечание. Число байтов в примере равно 3 (= 2 + 1), а не 5 (= 1 + 4), как было возвращено Marshal.SizeOf.

Примечание 2: sizeof не может определить размер параметров универсального типа.

Ответы [ 3 ]

4 голосов
/ 24 января 2010

Кажется, что нет документированного / публичного способа доступа к внутренней функции SizeOfType, используемой классом MemoryMappedViewAccessor, поэтому наиболее практичным способом получения размера этих структур будет использование отражения следующим образом:

static readonly Func<Type, uint> SizeOfType = (Func<Type, uint>)Delegate.CreateDelegate(typeof(Func<Type, uint>), typeof(Marshal).GetMethod("SizeOfType", BindingFlags.NonPublic | BindingFlags.Static));

static void Write<T1, T2>(T1 item1, T2 item2)
    where T1 : struct
    where T2 : struct
{
    using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32))
    using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
    {
        accessor.Write(0, ref item1);
        accessor.Write(SizeOfType(typeof(T1)), ref item2);
    }
}
2 голосов
/ 19 ноября 2011

Вы можете использовать Emit для доступа к коду операции Sizeof и обойти ограничение компилятора при получении sizeof (T):

var sizeOfMethod = new DynamicMethod(
    "GetManagedSizeImpl"
,   typeof(uint)
,   null
,   true);
var genSizeOf = sizeOfMethod.GetILGenerator();
genSizeOf.Emit(OpCodes.Sizeof, typeof(T));
genSizeOf.Emit(OpCodes.Ret);
var sizeOfFunction = (Func<uint>)sizeOfMethod.CreateDelegate(typeof(Func<uint>));

// ...
int size = checked((int)sizeOfFunction());
1 голос
/ 25 октября 2018

1. В одном ответе на этой странице предлагается использовать внутреннюю функцию Marshal.SizeOfType, но это работает только для структур, которые не содержат никаких управляемых ссылок.В .NET 4.7 он выдает ArgumentException при передаче ссылочного типа (class) или тип struct, содержащий встроенные ссылки.

2. Другой ответ здесь предлагает использовать код операции IL sizeof.Это работает корректно для всех типов значений struct, включая обобщенные и типы со встроенными ссылками, но для ссылочных типов всегда возвращается IntPtr.Size (, т. Е. значение 4 или 8), в отличие от фактического размера макета (экземпляра) управляемого класса.Это может быть то, что вы хотите, в зависимости от вашей ситуации.Обратите внимание, что результаты такого рода грациозно ухудшаются, объединяя случай структуры, содержащей одну встроенную ссылку (дескриптор) с одной ссылкой (дескриптор).

Более простой способ вызова инструкции sizeof ILчерез System.Runtime.CompilerServices.Unsafe пакет:

int struct_layout_bytes = Unsafe.Sizeof<T>();

3. Если вам действительно нужен фактический макетРазмер из (экземпляра) управляемого класса по какой-то причине, тогда вы, вероятно, делаете что-то не так, но вы можете получить это с помощью следующего, который работает только для ссылочных типов, то есть, когда typeof(T).IsValueTypefalse.

int class_layout_bytes = Marshal.ReadInt32(typeof(T).TypeHandle.Value, 4)

4. Поэтому - и все еще с предыдущим предостережением - чтобы получить размер макета экземпляра для любого типа ссылки или значения, включая те, которые содержат встроенные ссылки, из его ручки Type объедините методы # 2 и # 3:

int instance_layout_bytes = typeof(T).IsValueType ? 
                                Unsafe.Sizeof<T>() : 
                                Marshal.ReadInt32(typeof(T).TypeHandle.Value, 4);



относящиеся: Размер структуры с полями универсального типа

...