Как программа может получать метаданные классов после компиляции в C#
Что проверяют
Понимаете ли вы, что сборка .NET хранит не только IL, но и полные метаданные о типах, и как их читать. Следом обычно спрашивают про цену рефлексии и про то, что меняется при trimming и Native AOT.
Как отвечать
Компилятор кладёт в сборку таблицы метаданных по ECMA-335: типы, члены, сигнатуры, атрибуты. Программа читает их через рефлексию из System.Reflection:
- тип:
typeof(Order)на этапе компиляции,obj.GetType()для экземпляра,Type.GetType("MyApp.Order, MyApp")по строке. Без имени сборки поиск идёт только в вызывающей сборке и вSystem.Private.CoreLib; - члены:
GetProperties,GetMethods,GetFields,GetConstructors. Без флагов возвращаются только публичные, для приватных нужныBindingFlags.NonPublic | BindingFlags.Instance; - атрибуты:
GetCustomAttribute<T>()на типе или члене. На этом держатся сериализаторы, валидация, маршрутизация в ASP.NET Core; - сборка целиком:
Assembly.GetTypes(), например чтобы найти все реализации интерфейса для регистрации в DI; - без исполнения:
MetadataLoadContextисследует сборку, не запуская её код, аSystem.Reflection.Metadataдаёт низкоуровневый доступ к таблицам.
Затем про цену. MethodInfo.Invoke заметно медленнее прямого вызова, поэтому PropertyInfo кешируют, а горячие вызовы превращают в делегаты через CreateDelegate или деревья выражений. Современная альтернатива это source generators: метаданные читаются при компиляции, и в рантайме рефлексии нет. Так работает System.Text.Json в режиме генерации кода.
Где спотыкаются
Удивляются выводу GetMethods: там ToString, Equals, GetHashCode от object и геттеры вроде get_Number. Для чистого списка нужны BindingFlags.DeclaredOnly и фильтр по IsSpecialName.
Передают BindingFlags.NonPublic без Instance или Static и получают пустой массив.
Не знают, что при trimming и Native AOT неиспользуемые члены вырезаются, и рефлексия молча перестаёт их находить. Нужны аннотации [DynamicallyAccessedMembers] или переход на генераторы.