Рефлексия в C#: Runtime Type Discovery и динамическое программирование
Что такое рефлексия
Рефлексия (Reflection) — механизм получения информации о типах, членах, атрибутах и сборках во время выполнения. Позволяет: инспектировать метаданные, создавать экземпляры, вызывать методы, читать/писать поля/свойства динамически. Классы в System.Reflection.
Основные сценарии использования
- Чтение атрибутов во время выполнения
- Изучение типов в сборке и создание экземпляров
- Позднее связывание (late binding) — вызов методов по имени строки
- Создание типов динамически (
System.Reflection.Emit,AssemblyBuilder) - Сериализация, DI-контейнеры, ORM, тестовые фреймворки, плагины
Ключевые классы
| Класс | Назначение |
|---|---|
Type | Центральный класс — представляет тип (class, struct, interface, enum, delegate). Получение: typeof(MyClass), obj.GetType(), Type.GetType("FullName") |
Assembly | Сборка (.dll/.exe). Assembly.GetExecutingAssembly(), Assembly.LoadFrom(path) |
MemberInfo | Базовый для FieldInfo, PropertyInfo, MethodInfo, ConstructorInfo, EventInfo |
ParameterInfo | Информация о параметре метода |
CustomAttributeData | Данные атрибута без создания экземпляра атрибута |
Получение Type
// 1. typeof — compile-time тип
Type t1 = typeof(string);
// 2. GetType() — runtime тип объекта
object obj = "hello";
Type t2 = obj.GetType(); // System.String
// 3. По строке имени (сборка-полное имя)
Type t3 = Type.GetType("System.Int32, mscorlib"); // или без сборки для текущей
Type t4 = Assembly.GetExecutingAssembly().GetType("MyNamespace.MyClass");
Инспекция членов типа
Type t = typeof(MyClass);
// Публичные члены экземпляра
var methods = t.GetMethods(BindingFlags.Public | BindingFlags.Instance);
var properties = t.GetProperties();
var fields = t.GetFields();
var constructors = t.GetConstructors();
// Все члены (включая непубличные)
var allMethods = t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static);
Создание экземпляров (Activator)
// Через parameterless конструктор
object obj = Activator.CreateInstance(typeof(MyClass));
// Через конструктор с параметрами
object obj2 = Activator.CreateInstance(typeof(MyClass), args: new object[] { "param1", 42 });
// Generic версия (C# 2.0+)
MyClass instance = Activator.CreateInstance();
Вызов методов и доступ к свойствам/полям
Type t = typeof(MyClass);
object instance = Activator.CreateInstance(t);
// Вызов метода
MethodInfo method = t.GetMethod("MyMethod");
object result = method.Invoke(instance, new object[] { arg1, arg2 });
// Чтение/запись свойства
PropertyInfo prop = t.GetProperty("MyProperty");
prop.SetValue(instance, "new value");
object val = prop.GetValue(instance);
// Чтение/запись поля
FieldInfo field = t.GetField("myField");
field.SetValue(instance, 123);
Чтение атрибутов
[MyAttribute("value", Param = 42)]
public class MyClass { }
var attrs = typeof(MyClass).GetCustomAttributes(inherit: false);
foreach (var attr in attrs)
Console.WriteLine($"{attr.ConstructorArg}, {attr.Param}");
Пример из оригинала: чтение DebugInfo атрибутов
Type type = typeof(Rectangle);
// Атрибуты класса
foreach (object attrObj in type.GetCustomAttributes(false))
{
if (attrObj is DebugInfo dbi)
{
Console.WriteLine($"Bug: {dbi.BugNo}, Dev: {dbi.Developer}, Review: {dbi.LastReview}, Msg: {dbi.Message}");
}
}
// Атрибуты методов
foreach (MethodInfo m in type.GetMethods())
{
foreach (Attribute attr in m.GetCustomAttributes(true))
{
if (attr is DebugInfo dbi)
{
Console.WriteLine($"Method {m.Name}: Bug {dbi.BugNo}, Msg: {dbi.Message}");
}
}
}
Performance: кэширование и исходные генераторы
- Рефлексия медленнее прямого вызова. Кэшируйте
MethodInfo,PropertyInfoвstatic readonlyполях. - Для горячих путей используйте
Expression Trees→ компиляция в делегаты, илиSystem.Text.Jsonsource generators. Delegate.CreateDelegateдля быстрого вызова методов через делегат.
Безопасность
Рефлексия может обходить проверки доступа (BindingFlags.NonPublic). В .NET Core / .NET 5+ по умолчанию ограничена (требует ReflectionPermission в полной доверительной среде). Используйте осторожно.
Язык C
- C# Hello World — создание вашего первого C# приложения
- Понимание ключевых слов и идентификаторов C#: правила, списки и лучшие практики
- Мастерство C# переменных и примитивных типов данных: полное руководство
- Операторы C# — исчерпывающее руководство по операторам в C#
- Освоение побитовых и сдвиговых операторов в C#: полное руководство
- Основы ввода-вывода в C#: Console.WriteLine, ReadLine и форматирование
- Выражения, операторы и блоки в C#: подробное руководство с примерами
- Освоение комментариев в C#: виды, лучшие практики и XML-документация
- Оператор break в C#: как и когда использовать
- Оператор continue в C#: как эффективно пропускать итерации циклов