2015-08-06 4 views
1

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

Например: давайте возьмем простой код:

[StructLayout(LayoutKind.Auto)] 
public struct StructAutoLayout 
{ 
    byte B1; 
    long Long1; 
    byte B2; 
    long Long2; 
    byte B3; 
} 
public static void Main() 
{ 
    Console.WriteLine("Sizeof struct is {0}", Marshal.SizeOf<StructAutoLayout>()); 
} 

он бросает исключение:

Необработанное исключение: System.ArgumentException: Тип 'StructAutoLayout' не может быть выстраивали как неуправляемый состав; никакие значимые размеры или смещение не могут быть вычислены.

Значит, компилятор не знает размер структуры во время компиляции? Я был уверен, что этот атрибут переупорядочивает структуры полей, а затем компилирует их, но это не так.

ответ

4

Это не имеет никакого смысла. Маршаллинг используется для взаимодействия - и при выполнении взаимодействия обе стороны должны согласовать точно о структуре struct.

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

public struct StructAutoLayout 
{ 
    byte B1; 
    long Long1; 
    byte B2; 
    long Long2; 
    byte B3; 
} 

в то время как другие могут сделать что-то вроде этого:

public struct StructAutoLayout 
{ 
    byte B1; 
    byte B2; 
    byte B3; 
    byte _padding; 
    long Long1; 
    long Long2; 
} 

При работе с машинным/неуправляемым кодом, есть довольно много не мета-данные не участвуют - только указатели и значения. Другая сторона не знает, как структура фактически выложена, ожидает фиксированный макет, о котором вы оба заранее договорились.

. NET имеет тенденцию делать вас испорченными - почти все просто работает. Это не тот случай, когда вы сталкиваетесь с чем-то вроде C++ - если вы просто угадываете свой путь, вы, скорее всего, окажетесь с решением, которое обычно работает, но время от времени сбрасывает все ваше приложение. Выполняя что-либо с неуправляемым/родным кодом, убедитесь, что вы прекрасно понимаете, что вы делаете - неуправляемый interop просто хрупок.

В настоящее время класс Marshal рассчитан на для неуправляемого взаимодействия. Если вы прочитали документацию для Marshal.SizeOf, в ней конкретно указано

Возвращает размер неуправляемого типа в байтах.

И, конечно же,

Вы можете использовать этот метод, если у вас нет структуры. Макет должен быть последовательным или явным.

Возвращаемый размер является размером неуправляемого типа. Неуправляемые и управляемые размеры объекта могут отличаться. Для типов символов размер зависит от значения CharSet, применяемого к этому классу.

Если тип не может возможно быть ранжированы, что должно Marshal.SizeOf вернуться? Это даже не имеет смысла :)

Вопрос о размере типа или экземпляра не имеет никакого смысла в управляемой среде. «Реальный размер в памяти» - это деталь реализации, насколько вам известно - это не часть контракта, и на это нельзя положиться. Если требовалось время выполнения/компилятор, он мог бы сделать каждый byte 77 байтов, и он не нарушил бы какой-либо контракт вообще, пока он точно сохраняет значения от 0 до 255 точно.

Если вы использовали struct с явным (или последовательным) макетом, у вас будет определенный контракт на то, как будет выведен неуправляемый тип, и будет работать Marshal.SizeOf. Однако даже тогда он будет возвращать только размер неуправляемого типа, а не управляемый, который все еще может отличаться. И снова, оба могут быть разными в разных системах (например, будет четыре байта в 32-разрядной системе и восемь байтов в 64-разрядной системе при работе в качестве 64-разрядного приложения).

Еще один важный момент заключается в том, что в приложении .NET есть несколько уровней «компиляции». Первый уровень, используя компилятор C#, является только верхушкой айсберга - и это не часть, которая обрабатывает поля переупорядочения в структурах автоматической компоновки. Он просто отмечает структуру как «авто-макет», и все сделано. Фактическая компоновка обрабатывается при запуске приложения с помощью CLI (спецификация нечетна в том, обрабатывает ли JIT-компилятор это, но я бы так и предполагал). Но это не имеет никакого отношения к Marshal.SizeOf или даже к sizeof - оба из них все еще обрабатываются во время выполнения. Забудьте все, что вы знаете из C++ - C# (и даже C++/CLI) - совершенно другой зверь.

Если вам нужно профилировать управляемую память, используйте профилировщик памяти (например, CLRProfiler). Но поймите, что вы все еще занимаетесь профилированием памяти в очень специфической среде - разные системы или версии .NET могут дать вам разные результаты. И на самом деле нет ничего, говоря, что два экземпляра одной и той же структуры должны быть одного размера.

+0

«** runtime ** автоматически выбирает подходящий макет для членов объекта в неуправляемой памяти». Не компилятор. Это проблема. – Dennis

+0

@Dennis Ну, это обрабатывается CLI, но неясно, какая часть. Я бы ожидал, что компилятор JIT - тот, кто решает. Но это не имеет большого значения - даже если это был компилятор C#, это все равно означает, что тип будет опасным и бесполезным для неуправляемого взаимодействия. – Luaan

+0

Я согласен с тем, что не имеет смысла взаимодействовать со структурой, подобной этой. Но этот вопрос родился от другого: я просто не мог получить точный размер структуры. Я не хочу передавать его в приложение C++ или так далее, я просто удивился, что даже CLR не знает, что такое структура фактического размера. –

Смежные вопросы