Почему буферы фиксированного размера могут быть только примитивных типов?

Нам приходится много взаимодействовать с машинным кодом, и в этом случае гораздо быстрее использовать небезопасные структуры, не требующие маршалинга. Однако мы не можем этого сделать, если структуры содержат буферы фиксированного размера непримитивных типов. Почему компилятор C # требует, чтобы буферы фиксированного размера принадлежали только к примитивным типам? Почему буфер фиксированного размера не может состоять из такой структуры, как:

[StructLayout(LayoutKind.Sequential)]
struct SomeType
{
  int Number1;
  int Number2;
}

person jakobbotsch    schedule 16.09.2013    source источник
comment
Я собираюсь добавить сложности к компилятору. Компилятору необходимо будет проверить, не применялись ли какие-либо специфические функции .NET к структуре, применяемой к перечисляемым элементам. Например, универсальные шаблоны, реализация интерфейса, даже более глубокие свойства непримитивных массивов и т. Д. Несомненно, среда выполнения также будет иметь некоторые проблемы взаимодействия с подобными вещами.   -  person Simon Whitehead    schedule 17.09.2013
comment
Но это уже сделано компилятором. Если вы попытаетесь создать указатель на структуру, содержащую что-либо из этого, вы получите ошибку компилятора: msdn.microsoft.com/en-us/library/x2estayf (v = vs.90) .aspx   -  person jakobbotsch    schedule 17.09.2013
comment
Похоже, я неправильно понял, что вы имели в виду.   -  person Simon Whitehead    schedule 17.09.2013
comment
Вы можете ознакомиться с tutorials.csharp-online.net/   -  person    schedule 28.09.2013
comment
Но это уже сделано компилятором. Только частично. Компилятор может выполнять проверки, чтобы увидеть, управляется ли тип, но при этом не заботится о генерации кода для чтения / записи структур в фиксированные буферы. Это можно сделать (на уровне CIL ничто не мешает), это просто не реализовано в C #.   -  person Ibasa    schedule 30.09.2013
comment
Я думаю, это буквально потому, что они не хотят, чтобы вы использовали буферы фиксированного размера (потому что они хотят, чтобы вы использовали управляемый код). Слишком легкое взаимодействие с машинным кодом снижает вероятность использования .NET для всего, и они хотят максимально продвигать управляемый код.   -  person user541686    schedule 30.09.2013
comment
@ Mehrdad, это вроде теории заговора, тебе не кажется?   -  person Blorgbeard    schedule 01.10.2013
comment
@Blorgbeard: Конечно, но это единственное логическое объяснение, которое я могу придумать. Как еще вы объясните тот факт, что они затрудняют взаимодействие с машинным кодом на C #? Я приведу вам еще один пример: нет никаких технических причин, по которым универсальные типы не могут быть неуправляемыми, если они не содержат управляемых членов, - но они активно изо всех сил стараются запретить создание неуправляемых родные типы. Почему они это сделали? Очевидно, потому что они не хотят, чтобы вы использовали дженерики в неуправляемом коде. Но почему? Для меня наиболее логичным объяснением является то, что они, вероятно, продвигают управляемый код.   -  person user541686    schedule 01.10.2013


Ответы (3)


Буферы фиксированного размера в C # реализованы с помощью функции интерфейса командной строки, которая называется «непрозрачные классы». В разделе I.12.1.6.3 документа Ecma-335 они описаны:

Некоторые языки предоставляют многобайтовые структуры данных, содержимое которых обрабатывается напрямую с помощью адресной арифметики и косвенных операций. Для поддержки этой функции интерфейс командной строки позволяет создавать типы значений с указанным размером, но без информации об их элементах данных. Экземпляры этих «непрозрачных классов» обрабатываются точно так же, как экземпляры любого другого класса, но инструкции ldfld, stfld, ldflda, ldsfld и stsfld не должны использоваться для доступа к их содержимому.

«Нет информации об их элементах данных» и «ldfld / stfld не должны использоваться» - это руб. Второе правило накладывает запрет на структуры, вам нужны ldfld и stfld для доступа к их членам. Компилятор C # не может предоставить альтернативу, макет структуры является деталью реализации во время выполнения. Также причина того, что вы не можете использовать оператор sizeof в структуре. Decimal и Nullable ‹> отсутствуют, потому что они также являются структурами. IntPtr отсутствует, потому что его размер зависит от разрядности процесса, что затрудняет компилятору C # создание адреса для кода операции ldind / stind, используемого для доступа к буферу. Ссылки на ссылочные типы отсутствуют, потому что сборщик мусора должен иметь возможность найти их обратно и не может по первому правилу. Типы Enum имеют переменный размер, который зависит от их базового типа; звучит как решаемая проблема, не совсем понятно, почему они ее пропустили.

Остается только те, которые упомянуты в спецификации языка C #: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double или bool. Просто простые типы с четко определенным размером.

person Hans Passant    schedule 30.09.2013
comment
С каких это пор правда, что IntPtr отсутствует? struct S { IntPtr p1, p2, p3; } полностью неуправляемый, и вы можете sizeof(S) отлично справиться. Точно так же с struct P { void* p1, p2, p3; }, sizeof(P) и т. Д. Нет причин, по которым их следует запрещать (а это не так). Ваш ответ для меня не имеет смысла. - person user541686; 01.10.2013
comment
Кажется, вы путаете проблему размера с проблемой cannot-use-ldfld / stfld. Они не писали код для генерации адреса ldind / stind из выражения, использующего sizeof. Ответ Эрика Липперта на вопрос, почему это не было сделано, всегда один и тот же. - person Hans Passant; 01.10.2013
comment
Хотя я не гуру C #, обычно подобные спецификации на уровне виртуальных машин связаны с атомарностью, связанной с безопасностью потоков. ВМ может атомарно работать с примитивами, но не с любым типом структуры. Это единственный способ сохранить гарантии безопасности потоков. - person WeaponsGrade; 02.10.2013

Что такое фиксированный буфер?

Из MSDN:

В C # вы можете использовать оператор fixed для создания буфера с массивом фиксированного размера в структуре данных. Это полезно, когда вы работаете с существующим кодом, например кодом, написанным на других языках, уже существующими библиотеками DLL или COM-проектами. Фиксированный массив может принимать любые атрибуты или модификаторы, разрешенные для обычных членов структуры. Единственное ограничение - тип массива должен быть bool, byte, char, short, int, long, sbyte, ushort, uint, ulong, float или double.

Я просто процитирую г-на Ханса Пассана относительно того, почему фиксированный буфер ДОЛЖЕН быть unsafe. Вы можете увидеть Почему буферы фиксированного размера (массивы) должно быть небезопасно? для получения дополнительной информации.

Потому что «фиксированный буфер» - это не настоящий массив. Это настраиваемый тип значения, единственный известный мне способ его создания на языке C #. Среда CLR не может проверить безопасное выполнение индексации массива. Код также не поддается проверке. Наиболее наглядная демонстрация этого:

using System;

class Program {
    static unsafe void Main(string[] args) {
        var buf = new Buffer72();
        Console.WriteLine(buf.bs[8]);
        Console.ReadLine();
    }
}
public struct Buffer72 {
    public unsafe fixed byte bs[7];
}

В этом примере вы можете произвольно обращаться к кадру стека. Стандартный метод внедрения переполнения буфера будет доступен для вредоносного кода, чтобы исправить адрес возврата функции и заставить ваш код перейти в произвольное место.

Да, это небезопасно.

Почему фиксированный буфер не может содержать непримитивные типы данных?

Саймон Уайт поднял обоснованный вопрос:

Я собираюсь добавить «дополнительные сложности к компилятору». Компилятору необходимо будет проверить, не применялись ли какие-либо специфические функции .NET к структуре, применяемой к перечисляемым элементам. Например, универсальные шаблоны, реализация интерфейса, даже более глубокие свойства непримитивных массивов и т. Д. Несомненно, среда выполнения также будет иметь некоторые проблемы взаимодействия с подобными вещами.

И Ибаса:

«Но это уже сделано компилятором». Только частично. Компилятор может выполнять проверки, чтобы увидеть, управляется ли тип, но при этом не заботится о генерации кода для чтения / записи структур в фиксированные буферы. Это можно сделать (на уровне CIL ничто не мешает), это просто не реализовано в C #.

Наконец, Мехрдад:

Я думаю, это буквально потому, что они не хотят, чтобы вы использовали буферы фиксированного размера (потому что они хотят, чтобы вы использовали управляемый код). Слишком легкое взаимодействие с машинным кодом снижает вероятность использования .NET для всего, и они хотят максимально продвигать управляемый код.

Ответ звучит однозначно: «это просто не реализовано».

Почему не реализовано?

Мое предположение состоит в том, что затраты и время на внедрение для них просто не стоят того. Разработчики предпочли бы управляемый код неуправляемому. Возможно, это будет сделано в будущей версии C #, но текущая среда CLR лишена необходимой сложности.

Альтернативой может быть проблема безопасности. Поскольку фиксированные буферы чрезвычайно уязвимы для всевозможных проблем и угроз безопасности, если они будут плохо реализованы в вашем коде, я могу понять, почему их использование не рекомендуется по сравнению с управляемым кодом на C #. Зачем тратить много сил на то, от чего вы не хотите отказываться?

person DanteTheEgregore    schedule 30.09.2013

Я понимаю вашу точку зрения ... с другой стороны, я полагаю, что это может быть какая-то прямая совместимость, зарезервированная Microsoft. Ваш код скомпилирован в MSIL, и это дело конкретной .NET Framework и ОС, чтобы разместить его в памяти.

Я могу представить, что это может быть новый процессор от Intel, который потребует разметки переменных через каждые 8 ​​байтов для достижения оптимальной производительности. В этом случае в будущем, в некоторой будущей .NET Framework 6 и некоторой будущей Windows 9 потребуется разметка этих структур по-другому. В этом случае ваш примерный код заставит Microsoft не изменять структуру памяти в будущем и не ускорять платформу .NET до современного HW.

Это всего лишь предположение ...

Вы пробовали установить FieldOffset? См. объединение C ++ в C #

person Tomas Kubes    schedule 30.09.2013