Ненормальное программирование: обходим sizeof(T) == sizeof(IntPtr)
Если кто читал документацию MonoMod или активно писал под старый Mono, то может знать про очень неприятный баг: sizeof(T) всегда возвращает размер указателя вместо размера шаблонного типа.

Я об этом баге когда-то давно читал. А вчера решил исправить работу некоторых рулей (а-ля PXN v10 Pro) в одной игре на Unity 5, у которых сбит центр и доступный диапазон. Исправлять решил путём внедрения SharpDX.DirectInput, который хорошо работает со всеми такими рулями, но наткнулся на то, что часть его функций выбивает 0x80070057 (ERROR_INVALID_PARAMETER) и добраться до получения данных от руля нереально.
Через пару часов отладки обнаружил встроенную функцию дампа одной из структур, которую DI отказывается принимать, где меня ждал примерно такой сюрприз:

Только вместо правильного dwSize, dwObjSize и dwDataSize я получил сплошные восьмёрки. Ещё через минут 15 погуглил и вспомнил тот самый баг. И самое неприятное то, что он затрагивает все способы получить размер типа, даже Marshal.SizeOf не работает. И нормального решения нигде нет, если не учитывать шизофрению уровня бинарной сериализации, подсчёта размеров отдельных полей, вычитания адресов локальных переменных и генерации новых методов на лету, в который тип будет не шаблонный, а фиксированный.
И в данном случае можно было просто заменить всё на константы или же даже поискать другую библиотеку/сделать свою, но лёгкие пути ищут только слабаки!
И родила царица в ночь не то сына, не то дочь. Не обход и не хак, а неведомый костыль, аналогов которому в природе не имеется (если верить гуглу с яндексом).
Все готовы?
var monoClass = *(byte**)typeof(T).TypeHandle.Value;
var ptrSize = sizeof(IntPtr);
return *(int*)(monoClass + ptrSize * 3 + 4) - (ptrSize * 2);
Работает он очень просто: значение RuntimeTypeHandle указывает на неизвестную мне структуру, первым полем которой является ещё один указатель. Указатель ведёт на внутренний объект MonoClass. Первые 3 указателя в нём используются для наследования и не очень интересны (структуры по идее всегда должны иметь цепочку структура > ValueType > Object), потом идёт глубина наследования (2 байта, в случае структуры глубина = 3), размерность массива (1 байт), тип класса (1 байт, почему-то 0, хотя такое значение отсутствует в перечислении) и 4 байта размера объекта. Судя по всему, этот размер включает 2 указателя на таблицу методов и синхронизации, которые есть только у объектов классов и упаковок, поэтому для чистых структур нужно вычесть 2 указателя.
Да, не будет работать не в старом Mono, неизвестна работоспособность со встроенными типами, будет неправильно показывать размер объектов классов, возможно будет глючить в том случае, если тип ещё не был полноценно инициализирован, но мою проблему это решило. И, как мне кажется, решение красивее вышеперечисленных.
PS: Уже после написания поста обнаружил, что Marshal.SizeOf(typeof(T)) всё таки работает. Скорее всего я тогда пропатчил не тот DLL-файл, который тестировал. Он вычисляет размер немного по-другому, но в моём случае разницы нет

UPD: Та неизвестная структура скорее всего является структурой MonoType.


