Ответ
Reflection (отражение) в C# — это механизм времени выполнения, который позволяет инспектировать метаданные сборок, модулей и типов, а также динамически создавать экземпляры классов, вызывать методы и получать или устанавливать значения полей.
Основное применение:
- Динамическая загрузка сборок и создание экземпляров (например, для плагинов).
- Фреймворки сериализации/десериализации (например, JSON, XML).
- ORM (Object-Relational Mapper) для маппига свойств объектов на столбцы таблиц.
- Инструменты анализа кода и тестирования.
Пример использования:
using System.Reflection;
// Получаем тип класса string
Type stringType = typeof(string);
// Получаем метод ToUpper
MethodInfo toUpperMethod = stringType.GetMethod("ToUpper", new Type[0]);
// Вызываем метод на конкретном экземпляре
object result = toUpperMethod.Invoke("hello", null);
Console.WriteLine(result); // Выведет: HELLO
Ключевые нюансы и ограничения:
- Производительность: Операции рефлексии значительно медленнее статического вызова кода, так как требуют проверок во время выполнения. В критичных по производительности местах следует избегать рефлексии или кэшировать полученные объекты (например,
MethodInfo). - Безопасность: Рефлексия может нарушать инкапсуляцию, предоставляя доступ к приватным (
private) и защищённым (protected) членам при использовании флагаBindingFlags.NonPublic. Это требует соответствующих разрешений безопасности. - Исключения: Использование может приводить к исключениям времени выполнения, таким как
ArgumentNullException,MissingMethodException,TargetInvocationException. - Поддержка AOT: В средах с компиляцией Ahead-of-Time (AOT), таких как некоторые мобильные платформы или .NET Native, использование рефлексии может быть ограничено и требует специальной настройки.
Ответ 18+ 🔞
Давай разберём эту вашу рефлексию в C#, а то как будто про какую-то магию пишут, а на деле всё просто и местами даже больно.
Reflection (отражение) — это по сути твой внутренний шпион внутри программы. Во время работы она может сама себя ощупать, узнать, из каких классов состоит, какие в них методы прячутся, и даже вызвать их, хотя изначально про них нихрена не знала. Не статический вызов, а именно в рантайме.
Где это, блядь, применяется? Ну, например:
- Плагины. Загрузил сборку (DLL) как придётся, покопался в ней рефлексией, нашёл классы, которые от
IPluginнаследуются — и вот тебе, система расширений готова. - Сериализаторы (JSON, XML). Как думаешь,
System.Text.Jsonузнаёт, какие у твоего класса свойства? Правильно, рефлексией. Читает метаданные и пишет их в строку. - ORM вроде Entity Framework. Как он маппит свойства твоего
Userна столбцыUsersв базе? Опаньки, снова отражение — бежит по свойствам и смотрит, какие есть. - Тестовые раннеры (NUnit, xUnit). Как они находят все методы, помеченные
[Test]? Ну ты понял.
Вот тебе живой пример, смотри:
using System.Reflection;
// Берём тип, как он есть. Допустим, string
Type stringType = typeof(string);
// Ищем у него метод "ToUpper". Без параметров.
MethodInfo toUpperMethod = stringType.GetMethod("ToUpper", new Type[0]);
// Вызываем этот метод на реальной строке. Invoke — это и есть волшебный пендель.
object result = toUpperMethod.Invoke("hello", null);
Console.WriteLine(result); // Выведет: HELLO
Видишь? Мы взяли тип, вытащили из него метод как объект MethodInfo и дернули его. Как будто мы не знали на этапе компиляции, что у строки есть ToUpper.
А теперь, блядь, ложка дёгтя, и немалая:
-
Производительность — пиздец. Вызов через
Invoke— это не прямые скобочки(). Там куча проверок, распаковок. Это в сотни раз медленнее. Если делаешь в цикле на миллион итераций — забудь. Совет: если уж очень надо, найденныеMethodInfoилиPropertyInfoкэшируй в словарик, чтобы не искать каждый раз по новой. -
Безопасность. Рефлексия может залезть в
private-поле твоего класса и вытащить оттуда то, что ты прятал. Для этого естьBindingFlags.NonPublic. С одной стороны, мощно, с другой — это как дать ребёнку отвёртку и оставить его одного с розеткой. Нужны соответствующие права, иначе runtime тебя просто остановит. -
Исключения летят как из пулемёта. Передал не те аргументы в
Invoke— получиTargetInvocationExceptionилиArgumentException. Метод не нашёлся — ловиMissingMethodException. Код должен быть готов к этому говну. -
AOT (Ahead-of-Time компиляция) — отдельная песня. Это для iOS, некоторых сценариев .NET Native и т.д. Там компилятор заранее всё собирает в нативный код и выкидывает всё, что не используется статически. А если твоя рефлексия пытается найти тип по имени из конфига, которого на этапе компиляции не видно? Всё, приехали,
TypeLoadExceptionв рантайме. Приходится явно подсказывать линкеру, что трогать нельзя.
Итог: Инструмент хуёвой силы, но и ответственности. Пользоваться им нужно точечно, понимая цену, а не тыкать везде, где лень нормальный интерфейс или фабрику написать.