Custom Attributes в .NET — это, по мнению автора (разработчика библиотеки AsmResolver для парсинга .NET-бинарников), источник всего зла. Проблема не в самих атрибутах, а в том, как Microsoft спроектировала их хранение.
Первая головная боль — enum-значения. Представь: у атрибута есть параметр-перечисление. Как узнать, сколько байт он занимает в сигнатуре (1, 2, 4 или 8)? Никак — нужно разрешить тип самого enum. А это целая эпопея: найти сборку, где он определён (иногда через цепочку type forwarders), пройти по нескольким таблицам метаданных (TypeDef, NestedClass, Field), найти скрытое поле value__ и вычитать его сигнатуру. Это на порядки сложнее, чем прочитать один байт-индикатор (как ELEMENT_TYPE_I4), который всюду используется в остальном формате, но почему-то не здесь.
Вторая, ещё более чудовищная проблема — параметры типа Type и object. Вместо того чтобы использовать существующую систему метаданных с компактными индексами и токенами, Microsoft решила сохранять полностью квалифицированное имя (FQN) типа в виде строки. Это дико неэффективно. Одно typeof(int) превращается в строку 89 байт, которая к тому же не дедуплицируется — каждый атрибут копирует её заново. Для typeof(Dictionary<int, int>) получается ~~~300 символов. Сравни с двумя байтами ELEMENT_TYPE_I4, если бы использовали табличную систему.
Строки FQN не только пухлые, но и сложные для парсинга. У них своя грамматика с экранированием спецсимволов, правилами для версий, культуры и токена сборки. В разных версиях .NET эта грамматика ведёт себя по-разному. А главное — непредсказуемость разрешения без сборки: если не указать сборку, рантайм пытается угадать и часто ошибается. Пример: "System.IO.Stream" без сборки находится, а "System.Uri" — нет, хотя оба типа экспонируются одной и той же фасадной сборкой System.Runtime.dll. Это не интуитивно и требует погружения в детали реализации.
Автор называет этот дизайн "странным" и подозревает влияние подхода из JVM (где строки используются повсеместно). В остальном формат .NET продуман и стабилен, но Custom Attributes — "больное место". Microsoft вряд ли что-то изменит из-за обратной совместимости, хотя формат блоба предусматривает версионирование (сейчас 0x0001). Так что разработчикам парсеров (и самому автору AsmResolver) остаётся мириться с этим источником бесконечных багов.